diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index dedde7e07d..f1b63d7367 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -2,8 +2,6 @@ src/AddThreepid.js src/async-components/views/dialogs/EncryptedEventDialog.js -src/async-components/views/dialogs/ExportE2eKeysDialog.js -src/async-components/views/dialogs/ImportE2eKeysDialog.js src/autocomplete/AutocompleteProvider.js src/autocomplete/Autocompleter.js src/autocomplete/Components.js @@ -167,7 +165,6 @@ src/UserActivity.js src/utils/DecryptFile.js src/utils/DMRoomMap.js src/utils/FormattingUtils.js -src/utils/MegolmExportEncryption.js src/utils/MultiInviter.js src/utils/Receipt.js src/Velociraptor.js @@ -188,4 +185,3 @@ test/mock-clock.js test/skinned-sdk.js test/stores/RoomViewStore-test.js test/test-utils.js -test/utils/MegolmExportEncryption-test.js diff --git a/src/async-components/views/dialogs/ExportE2eKeysDialog.js b/src/async-components/views/dialogs/ExportE2eKeysDialog.js index d6f16a7105..045ea63c34 100644 --- a/src/async-components/views/dialogs/ExportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ExportE2eKeysDialog.js @@ -120,7 +120,7 @@ export default React.createClass({ 'you have received in encrypted rooms to a local file. You ' + 'will then be able to import the file into another Matrix ' + 'client in the future, so that client will also be able to ' + - 'decrypt these messages.' + 'decrypt these messages.', ) }

@@ -130,7 +130,7 @@ export default React.createClass({ 'careful to keep it secure. To help with this, you should enter ' + 'a passphrase below, which will be used to encrypt the exported ' + 'data. It will only be possible to import the data by using the ' + - 'same passphrase.' + 'same passphrase.', ) }

diff --git a/src/async-components/views/dialogs/ImportE2eKeysDialog.js b/src/async-components/views/dialogs/ImportE2eKeysDialog.js index 61d2aeec74..91010d33b9 100644 --- a/src/async-components/views/dialogs/ImportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/ImportE2eKeysDialog.js @@ -122,13 +122,13 @@ export default React.createClass({ 'This process allows you to import encryption keys ' + 'that you had previously exported from another Matrix ' + 'client. You will then be able to decrypt any ' + - 'messages that the other client could decrypt.' + 'messages that the other client could decrypt.', ) }

{ _t( 'The export file will be protected with a passphrase. ' + - 'You should enter the passphrase here, to decrypt the file.' + 'You should enter the passphrase here, to decrypt the file.', ) }

diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.js index c98c467e1c..de39ea71cc 100644 --- a/src/utils/MegolmExportEncryption.js +++ b/src/utils/MegolmExportEncryption.js @@ -32,7 +32,7 @@ const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle; /** * Decrypt a megolm key file * - * @param {ArrayBuffer} file + * @param {ArrayBuffer} data file to decrypt * @param {String} password * @return {Promise} promise for decrypted output */ @@ -61,12 +61,12 @@ export function decryptMegolmKeyFile(data, password) { const hmac = body.subarray(-32); return deriveKeys(salt, iterations, password).then((keys) => { - const [aes_key, hmac_key] = keys; + const [aesKey, hmacKey] = keys; const toVerify = body.subarray(0, -32); return subtleCrypto.verify( {name: 'HMAC'}, - hmac_key, + hmacKey, hmac, toVerify, ).then((isValid) => { @@ -80,7 +80,7 @@ export function decryptMegolmKeyFile(data, password) { counter: iv, length: 64, }, - aes_key, + aesKey, ciphertext, ); }); @@ -102,7 +102,7 @@ export function decryptMegolmKeyFile(data, password) { */ export function encryptMegolmKeyFile(data, password, options) { options = options || {}; - const kdf_rounds = options.kdf_rounds || 500000; + const kdfRounds = options.kdf_rounds || 500000; const salt = new Uint8Array(16); window.crypto.getRandomValues(salt); @@ -115,8 +115,8 @@ export function encryptMegolmKeyFile(data, password, options) { // of a single bit of iv is a price we have to pay. iv[9] &= 0x7f; - return deriveKeys(salt, kdf_rounds, password).then((keys) => { - const [aes_key, hmac_key] = keys; + return deriveKeys(salt, kdfRounds, password).then((keys) => { + const [aesKey, hmacKey] = keys; return subtleCrypto.encrypt( { @@ -124,7 +124,7 @@ export function encryptMegolmKeyFile(data, password, options) { counter: iv, length: 64, }, - aes_key, + aesKey, new TextEncoder().encode(data), ).then((ciphertext) => { const cipherArray = new Uint8Array(ciphertext); @@ -134,17 +134,17 @@ export function encryptMegolmKeyFile(data, password, options) { resultBuffer[idx++] = 1; // version resultBuffer.set(salt, idx); idx += salt.length; resultBuffer.set(iv, idx); idx += iv.length; - resultBuffer[idx++] = kdf_rounds >> 24; - resultBuffer[idx++] = (kdf_rounds >> 16) & 0xff; - resultBuffer[idx++] = (kdf_rounds >> 8) & 0xff; - resultBuffer[idx++] = kdf_rounds & 0xff; + resultBuffer[idx++] = kdfRounds >> 24; + resultBuffer[idx++] = (kdfRounds >> 16) & 0xff; + resultBuffer[idx++] = (kdfRounds >> 8) & 0xff; + resultBuffer[idx++] = kdfRounds & 0xff; resultBuffer.set(cipherArray, idx); idx += cipherArray.length; const toSign = resultBuffer.subarray(0, idx); return subtleCrypto.sign( {name: 'HMAC'}, - hmac_key, + hmacKey, toSign, ).then((hmac) => { hmac = new Uint8Array(hmac); @@ -170,7 +170,7 @@ function deriveKeys(salt, iterations, password) { new TextEncoder().encode(password), {name: 'PBKDF2'}, false, - ['deriveBits'] + ['deriveBits'], ).then((key) => { return subtleCrypto.deriveBits( { @@ -180,33 +180,33 @@ function deriveKeys(salt, iterations, password) { hash: 'SHA-512', }, key, - 512 + 512, ); }).then((keybits) => { const now = new Date(); console.log("E2e import/export: deriveKeys took " + (now - start) + "ms"); - const aes_key = keybits.slice(0, 32); - const hmac_key = keybits.slice(32); + const aesKey = keybits.slice(0, 32); + const hmacKey = keybits.slice(32); - const aes_prom = subtleCrypto.importKey( + const aesProm = subtleCrypto.importKey( 'raw', - aes_key, + aesKey, {name: 'AES-CTR'}, false, - ['encrypt', 'decrypt'] + ['encrypt', 'decrypt'], ); - const hmac_prom = subtleCrypto.importKey( + const hmacProm = subtleCrypto.importKey( 'raw', - hmac_key, + hmacKey, { name: 'HMAC', hash: {name: 'SHA-256'}, }, false, - ['sign', 'verify'] + ['sign', 'verify'], ); - return Promise.all([aes_prom, hmac_prom]); + return Promise.all([aesProm, hmacProm]); }); } @@ -301,7 +301,7 @@ function packMegolmKeyFile(data) { function encodeBase64(uint8Array) { // Misinterpt the Uint8Array as Latin-1. // window.btoa expects a unicode string with codepoints in the range 0-255. - var latin1String = String.fromCharCode.apply(null, uint8Array); + const latin1String = String.fromCharCode.apply(null, uint8Array); // Use the builtin base64 encoder. return window.btoa(latin1String); } @@ -313,10 +313,10 @@ function encodeBase64(uint8Array) { */ function decodeBase64(base64) { // window.atob returns a unicode string with codepoints in the range 0-255. - var latin1String = window.atob(base64); + const latin1String = window.atob(base64); // Encode the string as a Uint8Array - var uint8Array = new Uint8Array(latin1String.length); - for (var i = 0; i < latin1String.length; i++) { + const uint8Array = new Uint8Array(latin1String.length); + for (let i = 0; i < latin1String.length; i++) { uint8Array[i] = latin1String.charCodeAt(i); } return uint8Array; diff --git a/test/.eslintrc.js b/test/.eslintrc.js index 4cc4659d7d..537f989b62 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -2,4 +2,9 @@ module.exports = { env: { mocha: true, }, -} + + // mocha defines a 'this' + rules: { + "babel/no-invalid-this": "off", + }, +}; diff --git a/test/utils/MegolmExportEncryption-test.js b/test/utils/MegolmExportEncryption-test.js index 1c90c7a030..2637097837 100644 --- a/test/utils/MegolmExportEncryption-test.js +++ b/test/utils/MegolmExportEncryption-test.js @@ -25,23 +25,40 @@ const TEST_VECTORS=[ [ "plain", "password", - "-----BEGIN MEGOLM SESSION DATA-----\nAXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\ncissyYBxjsfsAndErh065A8=\n-----END MEGOLM SESSION DATA-----" + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" + + "cissyYBxjsfsAndErh065A8=\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "Hello, World", "betterpassword", - "-----BEGIN MEGOLM SESSION DATA-----\nAW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\nKYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n-----END MEGOLM SESSION DATA-----" + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "AW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" + + "KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "alphanumericallyalphanumericallyalphanumericallyalphanumerically", "SWORDFISH", - "-----BEGIN MEGOLM SESSION DATA-----\nAXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\nMgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\nPgg29363BGR+/Ripq/VCLKGNbw==\n-----END MEGOLM SESSION DATA-----" + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "AXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" + + "MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\n" + + "Pgg29363BGR+/Ripq/VCLKGNbw==\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "alphanumericallyalphanumericallyalphanumericallyalphanumerically", - "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword", - "-----BEGIN MEGOLM SESSION DATA-----\nAf//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\ngsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\nbWnSXS9oymiqwUIGs08sXI33ZA==\n-----END MEGOLM SESSION DATA-----" - ] + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpassword", + "-----BEGIN MEGOLM SESSION DATA-----\n" + + "Af//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\n" + + "gsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\n" + + "bWnSXS9oymiqwUIGs08sXI33ZA==\n" + + "-----END MEGOLM SESSION DATA-----", + ], ] ; @@ -55,7 +72,7 @@ describe('MegolmExportEncryption', function() { if (!window.crypto.subtle && !window.crypto.webkitSubtle) { this.skip(); } - }) + }); beforeEach(function() { testUtils.beforeEach(this); @@ -64,14 +81,14 @@ describe('MegolmExportEncryption', function() { describe('decrypt', function() { it('should handle missing header', function() { const input=stringToArray(`-----`); - expect(()=>{MegolmExportEncryption.decryptMegolmKeyFile(input, '')}) + expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) .toThrow('Header line not found'); }); it('should handle missing trailer', function() { const input=stringToArray(`-----BEGIN MEGOLM SESSION DATA----- -----`); - expect(()=>{MegolmExportEncryption.decryptMegolmKeyFile(input, '')}) + expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) .toThrow('Trailer line not found'); }); @@ -81,7 +98,7 @@ AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx cissyYBxjsfsAn -----END MEGOLM SESSION DATA----- `); - expect(()=>{MegolmExportEncryption.decryptMegolmKeyFile(input, '')}) + expect(()=>MegolmExportEncryption.decryptMegolmKeyFile(input, '')) .toThrow('Invalid file: too short'); }); @@ -94,12 +111,12 @@ cissyYBxjsfsAn const [plain, password, input] = TEST_VECTORS[i]; return MegolmExportEncryption.decryptMegolmKeyFile( - stringToArray(input), password + stringToArray(input), password, ).then((decrypted) => { expect(decrypted).toEqual(plain); return next(i+1); - }) - }; + }); + } return next(0).catch(done); }); }); @@ -115,7 +132,7 @@ cissyYBxjsfsAn input, password, {kdf_rounds: 1000}, ).then((ciphertext) => { return MegolmExportEncryption.decryptMegolmKeyFile( - ciphertext, password + ciphertext, password, ); }).then((plaintext) => { expect(plaintext).toEqual(input);