From 9ab169254417f97638e9019cfaf45943733def12 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 2 Apr 2019 14:30:41 +0200 Subject: [PATCH] fix verification, replace device id/key with SAS verification. --- src/scenarios/e2e-encryption.js | 24 +++++------- src/usecases/dialog.js | 23 ++++++----- src/usecases/memberlist.js | 22 +++++++++++ src/usecases/room-settings.js | 2 +- src/usecases/verify.js | 68 +++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 25 deletions(-) create mode 100644 src/usecases/verify.js diff --git a/src/scenarios/e2e-encryption.js b/src/scenarios/e2e-encryption.js index 7cd9f1ede9..c7a6a5d085 100644 --- a/src/scenarios/e2e-encryption.js +++ b/src/scenarios/e2e-encryption.js @@ -24,28 +24,24 @@ const invite = require('../usecases/invite'); const {receiveMessage} = require('../usecases/timeline'); const {createRoom} = require('../usecases/create-room'); const changeRoomSettings = require('../usecases/room-settings'); -const {getE2EDeviceFromSettings} = require('../usecases/settings'); -const {verifyDeviceForUser} = require('../usecases/memberlist'); +const {startSasVerifcation, acceptSasVerification} = require('../usecases/verify'); +const assert = require('assert'); module.exports = async function e2eEncryptionScenarios(alice, bob) { console.log(" creating an e2e encrypted room and join through invite:"); const room = "secrets"; await createRoom(bob, room); await changeRoomSettings(bob, {encryption: true}); + // await cancelKeyBackup(bob); await invite(bob, "@alice:localhost"); await acceptInvite(alice, room); - const bobDevice = await getE2EDeviceFromSettings(bob); - // wait some time for the encryption warning dialog - // to appear after closing the settings - await delay(1000); - await acceptDialogMaybe(bob, "encryption"); - const aliceDevice = await getE2EDeviceFromSettings(alice); - // wait some time for the encryption warning dialog - // to appear after closing the settings - await delay(1000); - await acceptDialogMaybe(alice, "encryption"); - await verifyDeviceForUser(bob, "alice", aliceDevice); - await verifyDeviceForUser(alice, "bob", bobDevice); + // do sas verifcation + bob.log.step(`starts SAS verification with ${alice.username}`); + const bobSasPromise = startSasVerifcation(bob, alice.username); + const aliceSasPromise = acceptSasVerification(alice, bob.username); + const [bobSas, aliceSas] = await Promise.all([bobSasPromise, aliceSasPromise]); + assert.deepEqual(bobSas, aliceSas); + bob.log.done(`done, (${bobSas.join(", ")}) matches!`); const aliceMessage = "Guess what I just heard?!" await sendMessage(alice, aliceMessage); await receiveMessage(bob, {sender: "alice", body: aliceMessage, encrypted: true}); diff --git a/src/usecases/dialog.js b/src/usecases/dialog.js index 89c70470d9..b11dac616d 100644 --- a/src/usecases/dialog.js +++ b/src/usecases/dialog.js @@ -16,27 +16,29 @@ limitations under the License. const assert = require('assert'); +async function assertDialog(session, expectedTitle) { + const titleElement = await session.waitAndQuery(".mx_Dialog .mx_Dialog_title"); + const dialogHeader = await session.innerText(titleElement); + assert(dialogHeader, expectedTitle); +} -async function acceptDialog(session, expectedContent) { - const foundDialog = await acceptDialogMaybe(session, expectedContent); +async function acceptDialog(session, expectedTitle) { + const foundDialog = await acceptDialogMaybe(session, expectedTitle); if (!foundDialog) { throw new Error("could not find a dialog"); } } -async function acceptDialogMaybe(session, expectedContent) { - let dialog = null; +async function acceptDialogMaybe(session, expectedTitle) { + let primaryButton = null; try { - dialog = await session.waitAndQuery(".mx_QuestionDialog"); + primaryButton = await session.waitAndQuery(".mx_Dialog [role=dialog] .mx_Dialog_primary"); } catch(err) { return false; } - if (expectedContent) { - const contentElement = await dialog.$(".mx_Dialog_content"); - const content = await (await contentElement.getProperty("innerText")).jsonValue(); - assert.ok(content.indexOf(expectedContent) !== -1); + if (expectedTitle) { + await assertDialog(session, expectedTitle); } - const primaryButton = await dialog.$(".mx_Dialog_primary"); await primaryButton.click(); return true; } @@ -44,4 +46,5 @@ async function acceptDialogMaybe(session, expectedContent) { module.exports = { acceptDialog, acceptDialogMaybe, + assertDialog, }; diff --git a/src/usecases/memberlist.js b/src/usecases/memberlist.js index b018ed552c..0cd8744853 100644 --- a/src/usecases/memberlist.js +++ b/src/usecases/memberlist.js @@ -16,6 +16,16 @@ limitations under the License. const assert = require('assert'); +async function openMemberInfo(session, name) { + const membersAndNames = await getMembersInMemberlist(session); + const matchingLabel = membersAndNames.filter((m) => { + return m.displayName === name; + }).map((m) => m.label)[0]; + await matchingLabel.click(); +}; + +module.exports.openMemberInfo = openMemberInfo; + module.exports.verifyDeviceForUser = async function(session, name, expectedDevice) { session.log.step(`verifies e2e device for ${name}`); const membersAndNames = await getMembersInMemberlist(session); @@ -23,8 +33,20 @@ module.exports.verifyDeviceForUser = async function(session, name, expectedDevic return m.displayName === name; }).map((m) => m.label)[0]; await matchingLabel.click(); + // click verify in member info const firstVerifyButton = await session.waitAndQuery(".mx_MemberDeviceInfo_verify"); await firstVerifyButton.click(); + // expect "Verify device" dialog and click "Begin Verification" + const dialogHeader = await session.innerText(await session.waitAndQuery(".mx_Dialog .mx_Dialog_title")); + assert(dialogHeader, "Verify device"); + const beginVerificationButton = await session.waitAndQuery(".mx_Dialog .mx_Dialog_primary") + await beginVerificationButton.click(); + // get emoji SAS labels + const sasLabelElements = await session.waitAndQueryAll(".mx_VerificationShowSas .mx_VerificationShowSas_emojiSas .mx_VerificationShowSas_emojiSas_label"); + const sasLabels = await Promise.all(sasLabelElements.map(e => session.innerText(e))); + console.log("my sas labels", sasLabels); + + const dialogCodeFields = await session.waitAndQueryAll(".mx_QuestionDialog code"); assert.equal(dialogCodeFields.length, 2); const deviceId = await session.innerText(dialogCodeFields[0]); diff --git a/src/usecases/room-settings.js b/src/usecases/room-settings.js index e204d24a52..95078d1c87 100644 --- a/src/usecases/room-settings.js +++ b/src/usecases/room-settings.js @@ -68,7 +68,7 @@ module.exports = async function changeRoomSettings(session, settings) { const clicked = await setSettingsToggle(session, e2eEncryptionToggle, settings.encryption); // if enabling, accept beta warning dialog if (clicked && settings.encryption) { - await acceptDialog(session, "encryption"); + await acceptDialog(session, "Enable encryption?"); } } diff --git a/src/usecases/verify.js b/src/usecases/verify.js new file mode 100644 index 0000000000..e9461c225e --- /dev/null +++ b/src/usecases/verify.js @@ -0,0 +1,68 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const assert = require('assert'); +const {openMemberInfo} = require("./memberlist"); +const {assertDialog, acceptDialog} = require("./dialog"); + +async function assertVerified(session) { + const dialogSubTitle = await session.innerText(await session.waitAndQuery(".mx_Dialog h2")); + assert(dialogSubTitle, "Verified!"); +} + +async function startVerification(session, name) { + await openMemberInfo(session, name); + // click verify in member info + const firstVerifyButton = await session.waitAndQuery(".mx_MemberDeviceInfo_verify"); + await firstVerifyButton.click(); +} + +async function getSasCodes(session) { + const sasLabelElements = await session.waitAndQueryAll(".mx_VerificationShowSas .mx_VerificationShowSas_emojiSas .mx_VerificationShowSas_emojiSas_label"); + const sasLabels = await Promise.all(sasLabelElements.map(e => session.innerText(e))); + return sasLabels; +} + +module.exports.startSasVerifcation = async function(session, name) { + await startVerification(session, name); + // expect "Verify device" dialog and click "Begin Verification" + await assertDialog(session, "Verify device"); + // click "Begin Verification" + await acceptDialog(session); + const sasCodes = await getSasCodes(session); + // click "Verify" + await acceptDialog(session); + await assertVerified(session); + // click "Got it" when verification is done + await acceptDialog(session); + return sasCodes; +}; + +module.exports.acceptSasVerification = async function(session, name) { + await assertDialog(session, "Incoming Verification Request"); + const opponentLabelElement = await session.query(".mx_IncomingSasDialog_opponentProfile h2"); + const opponentLabel = await session.innerText(opponentLabelElement); + assert(opponentLabel, name); + // click "Continue" button + await acceptDialog(session); + const sasCodes = await getSasCodes(session); + // click "Verify" + await acceptDialog(session); + await assertVerified(session); + // click "Got it" when verification is done + await acceptDialog(session); + return sasCodes; +};