Skip to content

Commit

Permalink
feat!: Allow E2EE rooms to reset its room key (#33328)
Browse files Browse the repository at this point in the history
Co-authored-by: Guilherme Gazzo <[email protected]>
Co-authored-by: Matheus Barbosa Silva <[email protected]>
  • Loading branch information
3 people authored Oct 14, 2024
1 parent 1343dff commit bfefe41
Show file tree
Hide file tree
Showing 20 changed files with 514 additions and 42 deletions.
11 changes: 11 additions & 0 deletions .changeset/smooth-horses-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@rocket.chat/meteor": major
"@rocket.chat/core-services": patch
"@rocket.chat/core-typings": patch
"@rocket.chat/ddp-client": patch
"@rocket.chat/i18n": patch
"@rocket.chat/model-typings": patch
"@rocket.chat/rest-typings": patch
---

Allows authorized users to reset the encryption key for end-to-end encrypted rooms. This aims to prevent situations where all users of a room have lost the encryption key, and as such, the access to the room.
42 changes: 42 additions & 0 deletions apps/meteor/app/api/server/v1/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ import {
ise2eUpdateGroupKeyParamsPOST,
isE2EProvideUsersGroupKeyProps,
isE2EFetchUsersWaitingForGroupKeyProps,
isE2EResetRoomKeyProps,
} from '@rocket.chat/rest-typings';
import ExpiryMap from 'expiry-map';
import { Meteor } from 'meteor/meteor';

import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { handleSuggestedGroupKey } from '../../../e2e/server/functions/handleSuggestedGroupKey';
import { provideUsersSuggestedGroupKeys } from '../../../e2e/server/functions/provideUsersSuggestedGroupKeys';
import { resetRoomKey } from '../../../e2e/server/functions/resetRoomKey';
import { settings } from '../../../settings/server';
import { API } from '../api';

// After 10s the room lock will expire, meaning that if for some reason the process never completed
// The next reset will be available 10s after
const LockMap = new ExpiryMap<string, boolean>(10000);

API.v1.addRoute(
'e2e.fetchMyKeys',
{
Expand Down Expand Up @@ -284,3 +293,36 @@ API.v1.addRoute(
},
},
);

// This should have permissions
API.v1.addRoute(
'e2e.resetRoomKey',
{ authRequired: true, validateParams: isE2EResetRoomKeyProps },
{
async post() {
const { rid, e2eKey, e2eKeyId } = this.bodyParams;
if (!(await hasPermissionAsync(this.userId, 'toggle-room-e2e-encryption', rid))) {
return API.v1.unauthorized();
}
if (LockMap.has(rid)) {
throw new Error('error-e2e-key-reset-in-progress');
}

LockMap.set(rid, true);

if (!(await canAccessRoomIdAsync(rid, this.userId))) {
throw new Error('error-not-allowed');
}

try {
await resetRoomKey(rid, this.userId, e2eKey, e2eKeyId);
return API.v1.success();
} catch (e) {
console.error(e);
return API.v1.failure('error-e2e-key-reset-failed');
} finally {
LockMap.delete(rid);
}
},
},
);
7 changes: 6 additions & 1 deletion apps/meteor/app/e2e/client/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,14 @@ export async function generateMnemonicPhrase(n, sep = ' ') {
return result.join(sep);
}

export async function createSha256Hash(data) {
export async function createSha256HashFromText(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('');
}

export async function sha256HashFromArrayBuffer(arrayBuffer) {
const hashArray = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', arrayBuffer)));
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}
Loading

0 comments on commit bfefe41

Please sign in to comment.