From 99c676bd9989c73cde149ab2948643aaf2440b8f Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 24 Sep 2018 13:48:41 -0300 Subject: [PATCH 1/9] Removed e2e password from alert message --- packages/rocketchat-e2e/client/rocketchat.e2e.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index 7faf96109c53..814c9d69b978 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -142,7 +142,7 @@ class E2E { if (randomPassword) { alerts.open({ title: TAPi18n.__('Save your encryption password'), - html: `
${ randomPassword }
This password will only show up this time. Click here to learn more.
`, + html: '
Click here to view and copy your password.
', modifiers: ['large'], closable: false, icon: 'key', From 9060cf1c6b75651ce6ddcf8390834b35bffb778b Mon Sep 17 00:00:00 2001 From: Pierre Date: Mon, 24 Sep 2018 14:42:53 -0300 Subject: [PATCH 2/9] Added strings to i18n --- .../rocketchat-e2e/client/rocketchat.e2e.js | 24 ++++++------------- packages/rocketchat-i18n/i18n/en.i18n.json | 2 ++ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index 814c9d69b978..cc06e8a02543 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -140,9 +140,14 @@ class E2E { const randomPassword = localStorage.getItem('e2e.randomPassword'); if (randomPassword) { + const passwordRevealText = TAPi18n.__('E2E_password_reveal_text', { + postProcess: 'sprintf', + sprintf: [randomPassword], + }); + alerts.open({ title: TAPi18n.__('Save your encryption password'), - html: '
Click here to view and copy your password.
', + html: TAPi18n.__('Click here to view and copy your password.'), modifiers: ['large'], closable: false, icon: 'key', @@ -150,22 +155,7 @@ class E2E { modal.open({ title: TAPi18n.__('Save your encryption password'), html: true, - text: ` -
- You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted. -
- 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. -
-
- Your password is: ${ randomPassword } -
-
- 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. -
- This password is only stored on this browser until you store the password and dismiss this message. -
- `, + text: `
${ passwordRevealText }
`, showConfirmButton: true, showCancelButton: true, confirmButtonText: TAPi18n.__('I saved my password, close this message'), diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 62ad563f91f3..17d795413a38 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -547,6 +547,7 @@ "clear_history": "Clear History", "Click_here": "Click here", "Click_here_for_more_info": "Click here for more info", + "Click here to view and copy your password.": "Click here to view and copy your password.", "Click_the_messages_you_would_like_to_send_by_email": "Click the messages you would like to send by e-mail", "Click_to_join": "Click to Join!", "Client_ID": "Client ID", @@ -976,6 +977,7 @@ "Duplicate_channel_name": "A Channel with name '%s' exists", "Duplicate_private_group_name": "A Private Group with name '%s' exists", "Duration": "Duration", + "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.
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.

Your password is: %s

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.
This password is only stored on this browser until you store the password and dismiss this message.", "Edit": "Edit", "edit-message": "Edit Message", "edit-message_description": "Permission to edit a message within a room", From a7d69b33edab024aae697398783d3f9954538e04 Mon Sep 17 00:00:00 2001 From: Pierre Date: Tue, 25 Sep 2018 11:58:11 -0300 Subject: [PATCH 3/9] Adjusted i18n strings --- packages/rocketchat-e2e/client/rocketchat.e2e.js | 10 +++++----- packages/rocketchat-e2e/server/settings.js | 1 + packages/rocketchat-i18n/i18n/en.i18n.json | 6 +++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index cc06e8a02543..a5f6818102aa 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -146,20 +146,20 @@ class E2E { }); alerts.open({ - title: TAPi18n.__('Save your encryption password'), - html: TAPi18n.__('Click here to view and copy your password.'), + title: TAPi18n.__('Save_your_encryption_password'), + html: TAPi18n.__('Click_here_to_view_and_copy_your_password'), modifiers: ['large'], closable: false, icon: 'key', action() { modal.open({ - title: TAPi18n.__('Save your encryption password'), + title: TAPi18n.__('Save_your_encryption_password'), html: true, text: `
${ passwordRevealText }
`, showConfirmButton: true, showCancelButton: true, - confirmButtonText: TAPi18n.__('I saved my password, close this message'), - cancelButtonText: TAPi18n.__('I\'ll do it later'), + confirmButtonText: TAPi18n.__('I_saved_my_password_close_this_message'), + cancelButtonText: TAPi18n.__('I_ll_do_it_later'), }, (confirm) => { if (!confirm) { return; diff --git a/packages/rocketchat-e2e/server/settings.js b/packages/rocketchat-e2e/server/settings.js index 48e946583641..37ec84992761 100644 --- a/packages/rocketchat-e2e/server/settings.js +++ b/packages/rocketchat-e2e/server/settings.js @@ -4,6 +4,7 @@ RocketChat.settings.addGroup('E2E Encryption', function() { this.add('E2E_Enable', false, { type: 'boolean', i18nLabel: 'Enabled', + i18nDescription: 'E2E_Enable_description', public: true, }); }); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 17d795413a38..2280d10cb87a 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -547,7 +547,7 @@ "clear_history": "Clear History", "Click_here": "Click here", "Click_here_for_more_info": "Click here for more info", - "Click here to view and copy your password.": "Click here to view and copy your password.", + "Click_here_to_view_and_copy_your_password": "Click here to view and copy your password.", "Click_the_messages_you_would_like_to_send_by_email": "Click the messages you would like to send by e-mail", "Click_to_join": "Click to Join!", "Client_ID": "Client ID", @@ -977,6 +977,7 @@ "Duplicate_channel_name": "A Channel with name '%s' exists", "Duplicate_private_group_name": "A Private Group with name '%s' exists", "Duration": "Duration", + "E2E_Enable_description": "This feature is still on BETA state.
Encrypted messages will not be found by search operations.
Notifications may also not work.", "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.
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.

Your password is: %s

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.
This password is only stored on this browser until you store the password and dismiss this message.", "Edit": "Edit", "edit-message": "Edit Message", @@ -1326,6 +1327,8 @@ "How_responsive_was_the_chat_agent": "How responsive was the chat agent?", "How_satisfied_were_you_with_this_chat": "How satisfied were you with this chat?", "How_to_handle_open_sessions_when_agent_goes_offline": "How to Handle Open Sessions When Agent Goes Offline", + "I_ll_do_it_later": "I'll do it later", + "I_saved_my_password_close_this_message": "I saved my password, close this message", "Idle_Time_Limit": "Idle Time Limit", "Idle_Time_Limit_Description": "Period of time until status changes to away. Value needs to be in seconds.", "if_they_are_from": "(if they are from %s)", @@ -2281,6 +2284,7 @@ "Save_changes": "Save changes", "Save_Mobile_Bandwidth": "Save Mobile Bandwidth", "Save_to_enable_this_action": "Save to enable this action", + "Save_your_encryption_password": "Save your encryption password", "Saved": "Saved", "Saving": "Saving", "Scan_QR_code": "Using an authenticator app like Google Authenticator, Authy or Duo, scan the QR code. It will display a 6 digit code which you need to enter below.", From 6652138e631897dd0b7b5a4971604cc8120082ae Mon Sep 17 00:00:00 2001 From: Pierre Date: Wed, 26 Sep 2018 13:30:25 -0300 Subject: [PATCH 4/9] Added a screen to allow users to change their encryption keys --- .../client/accountEncryption.html | 47 ++++++++++ .../client/accountEncryption.js | 87 +++++++++++++++++++ .../rocketchat-e2e/client/rocketchat.e2e.js | 20 ++++- packages/rocketchat-e2e/package.js | 13 ++- packages/rocketchat-i18n/i18n/en.i18n.json | 5 ++ .../client/accountFlex.html | 2 + 6 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 packages/rocketchat-e2e/client/accountEncryption.html create mode 100644 packages/rocketchat-e2e/client/accountEncryption.js diff --git a/packages/rocketchat-e2e/client/accountEncryption.html b/packages/rocketchat-e2e/client/accountEncryption.html new file mode 100644 index 000000000000..80e087574dc7 --- /dev/null +++ b/packages/rocketchat-e2e/client/accountEncryption.html @@ -0,0 +1,47 @@ + diff --git a/packages/rocketchat-e2e/client/accountEncryption.js b/packages/rocketchat-e2e/client/accountEncryption.js new file mode 100644 index 000000000000..de86f57cd15d --- /dev/null +++ b/packages/rocketchat-e2e/client/accountEncryption.js @@ -0,0 +1,87 @@ +import toastr from 'toastr'; +import _ from 'underscore'; +import s from 'underscore.string'; +import qrcode from 'yaqrcode'; +import { RocketChat } from 'meteor/rocketchat:lib'; +import { e2e } from 'meteor/rocketchat:e2e'; + +window.qrcode = qrcode; + +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 = ''; + }; + +}); diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index a5f6818102aa..5e4035fabacc 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -134,7 +134,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()), }); } @@ -189,6 +189,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'); @@ -238,11 +249,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 { diff --git a/packages/rocketchat-e2e/package.js b/packages/rocketchat-e2e/package.js index 84ff43a160fe..a0b7658f1e79 100644 --- a/packages/rocketchat-e2e/package.js +++ b/packages/rocketchat-e2e/package.js @@ -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'); }); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 2280d10cb87a..262d0156c421 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -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. https://github.com/RocketChat/hubot-rocketchat", "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. https://rocket.chat/docs/administrator-guides/integrations/zapier/using-zaps/", + "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", @@ -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", @@ -1030,6 +1032,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 change your encryption key because it was never used on this browser.", + "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", @@ -1886,6 +1890,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", diff --git a/packages/rocketchat-ui-account/client/accountFlex.html b/packages/rocketchat-ui-account/client/accountFlex.html index 68a5d6209fa0..dec79353b359 100644 --- a/packages/rocketchat-ui-account/client/accountFlex.html +++ b/packages/rocketchat-ui-account/client/accountFlex.html @@ -17,6 +17,8 @@

{{_ "My_Account"}}

{{/if}} {{> sidebarItem menuItem "Security" "lock" "account" "security" }} + + {{> sidebarItem menuItem "Encryption" "key" "account" "encryption" }} {{#if accessTokensEnabled}} {{> sidebarItem menuItem "Personal_Access_Tokens" "key" "account" "tokens" }} From c1afe51531626ef5aed822fe62642bb42bc6c76f Mon Sep 17 00:00:00 2001 From: Pierre Date: Wed, 26 Sep 2018 13:36:40 -0300 Subject: [PATCH 5/9] Removed unused import --- packages/rocketchat-e2e/client/accountEncryption.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/rocketchat-e2e/client/accountEncryption.js b/packages/rocketchat-e2e/client/accountEncryption.js index de86f57cd15d..7bf0badda817 100644 --- a/packages/rocketchat-e2e/client/accountEncryption.js +++ b/packages/rocketchat-e2e/client/accountEncryption.js @@ -1,5 +1,4 @@ import toastr from 'toastr'; -import _ from 'underscore'; import s from 'underscore.string'; import qrcode from 'yaqrcode'; import { RocketChat } from 'meteor/rocketchat:lib'; From 77bac519b97f4ed36bb4ad0a927522488747757e Mon Sep 17 00:00:00 2001 From: Pierre Date: Wed, 26 Sep 2018 13:42:27 -0300 Subject: [PATCH 6/9] Declared global variables --- packages/rocketchat-e2e/client/accountEncryption.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rocketchat-e2e/client/accountEncryption.js b/packages/rocketchat-e2e/client/accountEncryption.js index 7bf0badda817..7071d98aba5b 100644 --- a/packages/rocketchat-e2e/client/accountEncryption.js +++ b/packages/rocketchat-e2e/client/accountEncryption.js @@ -1,3 +1,4 @@ +/* globals Template, t, ReactiveVar */ import toastr from 'toastr'; import s from 'underscore.string'; import qrcode from 'yaqrcode'; From 8d2e228d1a924af2078bcdc7d4abcc08832218b0 Mon Sep 17 00:00:00 2001 From: Pierre Date: Wed, 26 Sep 2018 16:48:16 -0300 Subject: [PATCH 7/9] Added e2e explanation on the password change screen --- packages/rocketchat-e2e/client/accountEncryption.html | 4 ++++ packages/rocketchat-i18n/i18n/en.i18n.json | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/rocketchat-e2e/client/accountEncryption.html b/packages/rocketchat-e2e/client/accountEncryption.html index 80e087574dc7..4f70ef444625 100644 --- a/packages/rocketchat-e2e/client/accountEncryption.html +++ b/packages/rocketchat-e2e/client/accountEncryption.html @@ -13,6 +13,10 @@
+
+ {{{_ "E2E_Encryption_Password_Explanation" }}} +
+
{{#with canChange=allowKeyChange}}