Skip to content

New: Option to change E2E key #12169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions packages/rocketchat-e2e/client/accountEncryption.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template name="accountEncryption">
<section class="preferences-page preferences-page--new">
{{#header sectionName="Encryption" buttons=true fullpage=true}}
<div class="rc-header__section-button">
<button class="rc-button rc-button--primary" name="send" type="submit" data-button="create" form="encryption" {{canSave 'disabled'}}>{{_ "Save_changes"}}</button>
</div>
{{/header}}

<div class="preferences-page__content">
<form id="encryption" autocomplete="off" class="container">
{{# if isEnabled}}
<fieldset>
<div class="section">
<div class="section-content border-component-color">

<div class="alert">
{{{_ "E2E_Encryption_Password_Explanation" }}}
</div>

<div class="rc-input{{#if confirmationKeyInvalid}} rc-input--error{{/if}} rc-w50 padded">
{{#with canChange=allowKeyChange}}
<label class="rc-input__label">
<div class="rc-input__title">{{_ "New_encryption_key"}}</div>
<div class="rc-input__wrapper">
<input name="encryptionKey" type="password" class="rc-input__element" placeholder="{{_ "New_Password_Placeholder"}}" autocomplete="new-password" {{ifThenElse canChange '' 'disabled'}}>
</div>
</label>
{{#if canConfirmNewKey}}
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Confirm_new_encryption_key"}}</div>
<div class="rc-input__wrapper">
<input name="confirmation-encryptionKey" type="password" class="rc-input__element" placeholder="{{_ "Confirm_New_Password_Placeholder"}}" autocomplete="confirm-new-password">
</div>
</label>
{{/if}}
{{# unless canChange}}
<div class="rc-input__description">{{_ 'EncryptionKey_Change_Disabled'}}</div>
{{/unless}}
{{/with}}
</div>
</div>
</div>
</fieldset>

{{else}}
{{_ "Admin_disabled_encryption"}}
{{/if}}
</form>
</div>
</section>
</template>
84 changes: 84 additions & 0 deletions packages/rocketchat-e2e/client/accountEncryption.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* globals Template, t, ReactiveVar */
import toastr from 'toastr';
import s from 'underscore.string';
import { RocketChat } from 'meteor/rocketchat:lib';
import { e2e } from 'meteor/rocketchat:e2e';

Template.accountEncryption.helpers({
isEnabled() {
return RocketChat.settings.get('E2E_Enable');
},
allowKeyChange() {
return localStorage.getItem('public_key') && localStorage.getItem('private_key');
},
canConfirmNewKey() {
const encryptionKey = Template.instance().encryptionKey.get();
return encryptionKey && encryptionKey !== '';
},
ifThenElse(condition, val, not = '') {
return condition ? val : not;
},
canSave(ret) {
const instance = Template.instance();

const encryptionKey = instance.encryptionKey.get();
const confirmationEncryptionKey = instance.confirmationEncryptionKey.get();

if ((!encryptionKey || encryptionKey !== confirmationEncryptionKey)) {
return ret;
}
},
});

Template.accountEncryption.events({
'input [name=encryptionKey]'(e, instance) {
instance.encryptionKey.set(e.target.value);

if (e.target.value.length === 0) {
instance.confirmationEncryptionKey.set('');
}
},
'input [name=confirmation-encryptionKey]'(e, instance) {
instance.confirmationEncryptionKey.set(e.target.value);
},
'submit form'(e, instance) {
e.preventDefault();

return instance.save();
},
});

Template.accountEncryption.onCreated(function() {
const self = this;

this.encryptionKey = new ReactiveVar;
this.confirmationEncryptionKey = new ReactiveVar;

this.save = function(cb) {
const instance = this;
const data = {};

if (s.trim(self.encryptionKey.get())) {
data.newEncryptionKey = self.encryptionKey.get();
}

if (Object.keys(data).length === 0) {
return cb && cb();
}

e2e.changePassword(data.newEncryptionKey);

instance.clearForm();
toastr.remove();
this.encryptionKey.set('');
this.confirmationEncryptionKey.set('');

toastr.success(t('Encryption_key_saved_successfully'));
};

this.clearForm = function() {
this.find('[name=encryptionKey]').value = '';
this.find('[name=confirmation-encryptionKey]').value = '';
};

});
20 changes: 17 additions & 3 deletions packages/rocketchat-e2e/client/rocketchat.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class E2E {
if (!this.db_public_key || !this.db_private_key) {
await call('addKeyToChain', {
public_key: localStorage.getItem('public_key'),
private_key: await this.encodePrivateKey(localStorage.getItem('private_key')),
private_key: await this.encodePrivateKey(localStorage.getItem('private_key'), this.createRandomPassword()),
});
}

Expand Down Expand Up @@ -202,6 +202,17 @@ class E2E {
});
}

async changePassword(newPassword) {
await call('addKeyToChain', {
public_key: localStorage.getItem('public_key'),
private_key: await this.encodePrivateKey(localStorage.getItem('private_key'), newPassword),
});

if (localStorage.getItem('e2e.randomPassword')) {
localStorage.setItem('e2e.randomPassword', newPassword);
}
}

async loadKeysFromDB() {
try {
const { public_key, private_key } = await call('fetchMyKeys');
Expand Down Expand Up @@ -251,11 +262,14 @@ class E2E {
}
}

async encodePrivateKey(private_key) {
createRandomPassword() {
const randomPassword = `${ Random.id(3) }-${ Random.id(3) }-${ Random.id(3) }`.toLowerCase();
localStorage.setItem('e2e.randomPassword', randomPassword);
return randomPassword;
}

const masterKey = await this.getMasterKey(randomPassword);
async encodePrivateKey(private_key, password) {
const masterKey = await this.getMasterKey(password);

const vector = crypto.getRandomValues(new Uint8Array(16));
try {
Expand Down
13 changes: 10 additions & 3 deletions packages/rocketchat-e2e/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ Package.describe({
});

Package.onUse(function(api) {
api.use('ecmascript');
api.use('less');
api.use('mizzao:timesync');
api.use([
'ecmascript',
'less',
'mizzao:timesync',
'rocketchat:lib',
'templating',
'sha',
]);

api.mainModule('client/rocketchat.e2e.js', 'client');
api.addFiles('client/accountEncryption.html', 'client');
api.addFiles('client/accountEncryption.js', 'client');

api.mainModule('server/index.js', 'server');
});
6 changes: 6 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@
"Additional_Feedback": "Additional Feedback",
"additional_integrations_Bots": "If you are looking for how to integrate your own bot, then look no further than our Hubot adapter. <a href='https://github.com/RocketChat/hubot-rocketchat' target='_blank'>https://github.com/RocketChat/hubot-rocketchat</a>",
"additional_integrations_Zapier": "Are you looking to integrate other software and applications with Rocket.Chat but you don't have the time to manually do it? Then we suggest using Zapier which we fully support. Read more about it on our documentation. <a href='https://rocket.chat/docs/administrator-guides/integrations/zapier/using-zaps/' target='_blank'>https://rocket.chat/docs/administrator-guides/integrations/zapier/using-zaps/</a>",
"Admin_disabled_encryption": "Your administrator did not enable E2E encryption.",
"Admin_Info": "Admin Info",
"Administration": "Administration",
"Adult_images_are_not_allowed": "Adult images are not allowed",
Expand Down Expand Up @@ -572,6 +573,7 @@
"Compact": "Compact",
"Condensed": "Condensed",
"Computer": "Computer",
"Confirm_new_encryption_key": "Confirm new encryption key",
"Confirm_password": "Confirm your password",
"Connection_Closed": "Connection closed",
"Connection_Reset": "Connection reset",
Expand Down Expand Up @@ -978,6 +980,7 @@
"Duplicate_private_group_name": "A Private Group with name '%s' exists",
"Duration": "Duration",
"E2E_Enable_description": "This feature is still on BETA state.<br/>Encrypted messages will not be found by search operations.<br/> Notifications may also not work.",
"E2E_Encryption_Password_Explanation": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.<br/><br/>This is end to end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store your password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.",
"E2E_password_reveal_text": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.<br/>This is end to end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store this password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.<br/><br/>Your password is: <span style=\"font-weight: bold;\">%s</span><br/><br/>This is an auto generated password, you can setup a new password for your encryption key any time from any browser you have entered the existing password.<br/>This password is only stored on this browser until you store the password and dismiss this message.",
"Edit": "Edit",
"edit-message": "Edit Message",
Expand Down Expand Up @@ -1030,6 +1033,8 @@
"Encrypted": "Encrypted",
"Encrypted_channel_Description": "End to end encrypted channel. Search will not work with encrypted channels and notifications may not show the messages content.",
"Encrypted_message": "Encrypted message",
"EncryptionKey_Change_Disabled": "You can't set a password for your encryption key because your private key is not present on this client. In order to set a new password you need load your private key using your existing password or use a client where the key is already loaded.",
"Encryption_key_saved_successfully": "Your encryption key was saved successfully.",
"End_OTR": "End OTR",
"Enter_a_name": "Enter a name",
"Enter_a_regex": "Enter a regex",
Expand Down Expand Up @@ -1886,6 +1891,7 @@
"New_Password_Placeholder": "Please enter new password...",
"Confirm_new_password": "Confirm New Password",
"Confirm_New_Password_Placeholder": "Please re-enter new password...",
"New_encryption_key": "New encryption key",
"New_role": "New role",
"New_Room_Notification": "New Room Notification",
"New_Trigger": "New Trigger",
Expand Down
4 changes: 4 additions & 0 deletions packages/rocketchat-ui-account/client/accountFlex.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ <h1 class="sidebar-flex__title">{{_ "My_Account"}}</h1>
{{/if}}

{{> sidebarItem menuItem "Security" "lock" "account" "security" }}

{{#if encryptionEnabled}}
{{> sidebarItem menuItem "Encryption" "key" "account" "encryption" }}
{{/if}}

{{#if accessTokensEnabled}}
{{> sidebarItem menuItem "Personal_Access_Tokens" "key" "account" "tokens" }}
Expand Down
3 changes: 3 additions & 0 deletions packages/rocketchat-ui-account/client/accountFlex.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ Template.accountFlex.helpers({
accessTokensEnabled() {
return RocketChat.settings.get('API_Enable_Personal_Access_Tokens');
},
encryptionEnabled() {
return RocketChat.settings.get('E2E_Enable');
},
menuItem(name, icon, section, group) {
return {
name: t(name),
Expand Down