2022-05-30 13:26:48 +00:00
/ *
Copyright 2022 The Matrix . org Foundation C . I . C .
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 .
* /
2022-11-25 13:54:06 +00:00
import type { ISendEventResponse , MatrixClient , Room } from "matrix-js-sdk/src/matrix" ;
2023-06-14 14:35:32 +00:00
import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api" ;
2022-12-16 14:59:03 +00:00
import type { CypressBot } from "../../support/bot" ;
2023-01-10 23:29:56 +00:00
import { HomeserverInstance } from "../../plugins/utils/homeserver" ;
2023-01-19 15:19:07 +00:00
import { UserCredentials } from "../../support/login" ;
2023-09-05 11:11:10 +00:00
import {
2023-10-02 19:08:41 +00:00
createSharedRoomWithUser ,
2023-09-05 11:11:10 +00:00
doTwoWaySasVerification ,
downloadKey ,
enableKeyBackup ,
logIntoElement ,
logOutOfElement ,
waitForVerificationRequest ,
} from "./utils" ;
2023-05-24 09:50:05 +00:00
import { skipIfRustCrypto } from "../../support/util" ;
2022-05-30 13:26:48 +00:00
2022-06-30 08:59:25 +00:00
interface CryptoTestContext extends Mocha . Context {
2023-01-10 23:29:56 +00:00
homeserver : HomeserverInstance ;
2022-12-16 14:59:03 +00:00
bob : CypressBot ;
2022-06-30 08:59:25 +00:00
}
const openRoomInfo = ( ) = > {
2023-05-01 20:13:24 +00:00
cy . findByRole ( "button" , { name : "Room info" } ) . click ( ) ;
2022-06-30 08:59:25 +00:00
return cy . get ( ".mx_RightPanel" ) ;
} ;
2022-05-30 13:26:48 +00:00
2022-06-30 08:59:25 +00:00
const checkDMRoom = ( ) = > {
2023-04-12 15:48:31 +00:00
cy . get ( ".mx_RoomView_body" ) . within ( ( ) = > {
cy . findByText ( "Alice created this DM." ) . should ( "exist" ) ;
cy . findByText ( "Alice invited Bob" , { timeout : 1000 } ) . should ( "exist" ) ;
cy . get ( ".mx_cryptoEvent" ) . within ( ( ) = > {
cy . findByText ( "Encryption enabled" ) . should ( "exist" ) ;
} ) ;
} ) ;
2022-06-30 08:59:25 +00:00
} ;
const startDMWithBob = function ( this : CryptoTestContext ) {
2023-04-12 15:48:31 +00:00
cy . get ( ".mx_RoomList" ) . within ( ( ) = > {
cy . findByRole ( "button" , { name : "Start chat" } ) . click ( ) ;
} ) ;
cy . findByTestId ( "invite-dialog-input" ) . type ( this . bob . getUserId ( ) ) ;
cy . get ( ".mx_InviteDialog_tile_nameStack_name" ) . within ( ( ) = > {
cy . findByText ( "Bob" ) . click ( ) ;
} ) ;
cy . get ( ".mx_InviteDialog_userTile_pill .mx_InviteDialog_userTile_name" ) . within ( ( ) = > {
cy . findByText ( "Bob" ) . should ( "exist" ) ;
} ) ;
cy . findByRole ( "button" , { name : "Go" } ) . click ( ) ;
2022-06-30 08:59:25 +00:00
} ;
const testMessages = function ( this : CryptoTestContext ) {
2022-08-04 06:19:52 +00:00
// check the invite message
2023-04-12 15:48:31 +00:00
cy . findByText ( "Hey!" )
2022-08-25 09:46:43 +00:00
. closest ( ".mx_EventTile" )
. within ( ( ) = > {
cy . get ( ".mx_EventTile_e2eIcon_warning" ) . should ( "not.exist" ) ;
} ) ;
2022-06-30 08:59:25 +00:00
// Bob sends a response
cy . get < Room > ( "@bobsRoom" ) . then ( ( room ) = > {
this . bob . sendTextMessage ( room . roomId , "Hoo!" ) ;
2022-05-30 13:26:48 +00:00
} ) ;
2023-04-12 15:48:31 +00:00
cy . findByText ( "Hoo!" ) . closest ( ".mx_EventTile" ) . should ( "not.have.descendants" , ".mx_EventTile_e2eIcon_warning" ) ;
2022-06-30 08:59:25 +00:00
} ;
2022-05-30 13:26:48 +00:00
2022-06-30 08:59:25 +00:00
const bobJoin = function ( this : CryptoTestContext ) {
2022-10-07 11:34:28 +00:00
cy . window ( { log : false } )
. then ( async ( win ) = > {
const bobRooms = this . bob . getRooms ( ) ;
if ( ! bobRooms . length ) {
await new Promise < void > ( ( resolve ) = > {
const onMembership = ( _event ) = > {
this . bob . off ( win . matrixcs . RoomMemberEvent . Membership , onMembership ) ;
resolve ( ) ;
} ;
this . bob . on ( win . matrixcs . RoomMemberEvent . Membership , onMembership ) ;
} ) ;
}
} )
. then ( ( ) = > {
cy . botJoinRoomByName ( this . bob , "Alice" ) . as ( "bobsRoom" ) ;
} ) ;
2023-04-12 15:48:31 +00:00
cy . findByText ( "Bob joined the room" ) . should ( "exist" ) ;
2022-06-30 08:59:25 +00:00
} ;
2022-11-30 13:03:47 +00:00
/** configure the given MatrixClient to auto-accept any invites */
function autoJoin ( client : MatrixClient ) {
cy . window ( { log : false } ) . then ( async ( win ) = > {
client . on ( win . matrixcs . RoomMemberEvent . Membership , ( event , member ) = > {
if ( member . membership === "invite" && member . userId === client . getUserId ( ) ) {
client . joinRoom ( member . roomId ) ;
}
} ) ;
} ) ;
}
2022-06-30 08:59:25 +00:00
const verify = function ( this : CryptoTestContext ) {
const bobsVerificationRequestPromise = waitForVerificationRequest ( this . bob ) ;
openRoomInfo ( ) . within ( ( ) = > {
2023-04-12 15:48:31 +00:00
cy . findByRole ( "button" , { name : /People \d/ } ) . click ( ) ; // \d is the number of the room members
cy . findByText ( "Bob" ) . click ( ) ;
cy . findByRole ( "button" , { name : "Verify" } ) . click ( ) ;
cy . findByRole ( "button" , { name : "Start Verification" } ) . click ( ) ;
2023-06-19 12:37:28 +00:00
// this requires creating a DM, so can take a while. Give it a longer timeout.
cy . findByRole ( "button" , { name : "Verify by emoji" , timeout : 30000 } ) . click ( ) ;
2023-07-07 16:56:53 +00:00
cy . wrap ( bobsVerificationRequestPromise ) . then ( async ( request : VerificationRequest ) = > {
2023-06-22 09:43:49 +00:00
// the bot user races with the Element user to hit the "verify by emoji" button
2023-07-07 16:56:53 +00:00
const verifier = await request . startVerification ( "m.sas.v1" ) ;
2023-06-22 09:43:49 +00:00
doTwoWaySasVerification ( verifier ) ;
2022-06-30 08:59:25 +00:00
} ) ;
2023-04-12 15:48:31 +00:00
cy . findByRole ( "button" , { name : "They match" } ) . click ( ) ;
cy . findByText ( "You've successfully verified Bob!" ) . should ( "exist" ) ;
cy . findByRole ( "button" , { name : "Got it" } ) . click ( ) ;
2022-05-30 13:26:48 +00:00
} ) ;
2022-06-30 08:59:25 +00:00
} ;
2022-05-30 13:26:48 +00:00
2022-06-30 08:59:25 +00:00
describe ( "Cryptography" , function ( ) {
2023-01-19 15:19:07 +00:00
let aliceCredentials : UserCredentials ;
2023-09-05 11:11:10 +00:00
let homeserver : HomeserverInstance ;
let bob : CypressBot ;
2023-01-19 15:19:07 +00:00
2022-06-30 08:59:25 +00:00
beforeEach ( function ( ) {
2023-01-10 23:29:56 +00:00
cy . startHomeserver ( "default" )
. as ( "homeserver" )
2023-09-05 11:11:10 +00:00
. then ( ( data ) = > {
homeserver = data ;
2023-01-19 15:19:07 +00:00
cy . initTestUser ( homeserver , "Alice" , undefined , "alice_" ) . then ( ( credentials ) = > {
aliceCredentials = credentials ;
} ) ;
2023-09-05 11:11:10 +00:00
return cy . getBot ( homeserver , {
2023-05-25 18:24:50 +00:00
displayName : "Bob" ,
autoAcceptInvites : false ,
userIdPrefix : "bob_" ,
2023-09-05 11:11:10 +00:00
} ) ;
} )
. as ( "bob" )
. then ( ( data ) = > {
bob = data ;
2022-05-30 13:26:48 +00:00
} ) ;
2022-06-30 08:59:25 +00:00
} ) ;
afterEach ( function ( this : CryptoTestContext ) {
2023-01-10 23:29:56 +00:00
cy . stopHomeserver ( this . homeserver ) ;
2022-06-30 08:59:25 +00:00
} ) ;
2022-05-30 13:26:48 +00:00
2023-07-27 11:34:44 +00:00
for ( const isDeviceVerified of [ true , false ] ) {
it ( ` setting up secure key backup should work isDeviceVerified= ${ isDeviceVerified } ` , ( ) = > {
2023-06-20 15:27:18 +00:00
/ * *
* Verify that the ` m.cross_signing. ${ keyType } ` key is available on the account data on the server
* @param keyType
* /
function verifyKey ( keyType : string ) {
return cy
. getClient ( )
. then ( ( cli ) = > cy . wrap ( cli . getAccountDataFromServer ( ` m.cross_signing. ${ keyType } ` ) ) )
. then ( ( accountData : { encrypted : Record < string , Record < string , string > > } ) = > {
expect ( accountData . encrypted ) . to . exist ;
const keys = Object . keys ( accountData . encrypted ) ;
const key = accountData . encrypted [ keys [ 0 ] ] ;
expect ( key . ciphertext ) . to . exist ;
expect ( key . iv ) . to . exist ;
expect ( key . mac ) . to . exist ;
2023-06-14 08:12:01 +00:00
} ) ;
2023-06-20 15:27:18 +00:00
}
2023-06-14 08:12:01 +00:00
2023-06-20 15:27:18 +00:00
it ( "by recovery code" , ( ) = > {
// Verified the device
if ( isDeviceVerified ) {
cy . bootstrapCrossSigning ( aliceCredentials ) ;
}
cy . openUserSettings ( "Security & Privacy" ) ;
cy . findByRole ( "button" , { name : "Set up Secure Backup" } ) . click ( ) ;
cy . get ( ".mx_Dialog" ) . within ( ( ) = > {
// Recovery key is selected by default
cy . findByRole ( "button" , { name : "Continue" } ) . click ( ) ;
cy . get ( ".mx_CreateSecretStorageDialog_recoveryKey code" ) . invoke ( "text" ) . as ( "securityKey" ) ;
downloadKey ( ) ;
// When the device is verified, the `Setting up keys` step is skipped
if ( ! isDeviceVerified ) {
cy . get ( ".mx_InteractiveAuthDialog" ) . within ( ( ) = > {
cy . get ( ".mx_Dialog_title" ) . within ( ( ) = > {
cy . findByText ( "Setting up keys" ) . should ( "exist" ) ;
cy . findByText ( "Setting up keys" ) . should ( "not.exist" ) ;
} ) ;
} ) ;
}
cy . findByText ( "Secure Backup successful" ) . should ( "exist" ) ;
cy . findByRole ( "button" , { name : "Done" } ) . click ( ) ;
cy . findByText ( "Secure Backup successful" ) . should ( "not.exist" ) ;
} ) ;
2023-06-14 08:12:01 +00:00
2023-06-20 15:27:18 +00:00
// Verify that the SSSS keys are in the account data stored in the server
verifyKey ( "master" ) ;
verifyKey ( "self_signing" ) ;
verifyKey ( "user_signing" ) ;
2023-04-12 15:48:31 +00:00
} ) ;
2023-02-17 12:35:13 +00:00
2023-06-20 15:27:18 +00:00
it ( "by passphrase" , ( ) = > {
// Verified the device
if ( isDeviceVerified ) {
cy . bootstrapCrossSigning ( aliceCredentials ) ;
}
cy . openUserSettings ( "Security & Privacy" ) ;
cy . findByRole ( "button" , { name : "Set up Secure Backup" } ) . click ( ) ;
cy . get ( ".mx_Dialog" ) . within ( ( ) = > {
// Select passphrase option
cy . findByText ( "Enter a Security Phrase" ) . click ( ) ;
cy . findByRole ( "button" , { name : "Continue" } ) . click ( ) ;
// Fill passphrase input
cy . get ( "input" ) . type ( "new passphrase for setting up a secure key backup" ) ;
cy . contains ( ".mx_Dialog_primary:not([disabled])" , "Continue" ) . click ( ) ;
// Confirm passphrase
cy . get ( "input" ) . type ( "new passphrase for setting up a secure key backup" ) ;
cy . contains ( ".mx_Dialog_primary:not([disabled])" , "Continue" ) . click ( ) ;
downloadKey ( ) ;
cy . findByText ( "Secure Backup successful" ) . should ( "exist" ) ;
cy . findByRole ( "button" , { name : "Done" } ) . click ( ) ;
cy . findByText ( "Secure Backup successful" ) . should ( "not.exist" ) ;
} ) ;
// Verify that the SSSS keys are in the account data stored in the server
verifyKey ( "master" ) ;
verifyKey ( "self_signing" ) ;
verifyKey ( "user_signing" ) ;
} ) ;
2023-07-27 11:34:44 +00:00
} ) ;
}
2022-05-30 13:26:48 +00:00
2022-06-30 08:59:25 +00:00
it ( "creating a DM should work, being e2e-encrypted / user verification" , function ( this : CryptoTestContext ) {
2023-10-05 14:30:49 +00:00
skipIfRustCrypto ( ) ; // https://github.com/vector-im/element-web/issues/25618
2023-01-19 15:19:07 +00:00
cy . bootstrapCrossSigning ( aliceCredentials ) ;
2022-06-30 08:59:25 +00:00
startDMWithBob . call ( this ) ;
2022-08-04 06:19:52 +00:00
// send first message
2023-04-12 15:48:31 +00:00
cy . findByRole ( "textbox" , { name : "Send a message…" } ) . type ( "Hey!{enter}" ) ;
2022-06-30 08:59:25 +00:00
checkDMRoom ( ) ;
bobJoin . call ( this ) ;
testMessages . call ( this ) ;
verify . call ( this ) ;
2023-05-08 02:26:29 +00:00
// Assert that verified icon is rendered
cy . findByRole ( "button" , { name : "Room members" } ) . click ( ) ;
cy . findByRole ( "button" , { name : "Room information" } ) . click ( ) ;
cy . get ( ".mx_RoomSummaryCard_e2ee_verified" ) . should ( "exist" ) ;
// Take a snapshot of RoomSummaryCard with a verified E2EE icon
cy . get ( ".mx_RightPanel" ) . percySnapshotElement ( "RoomSummaryCard - with a verified E2EE icon" , {
widths : [ 264 ] , // Emulate the UI. The value is based on minWidth specified on MainSplit.tsx
} ) ;
2022-05-30 13:26:48 +00:00
} ) ;
2022-11-30 13:03:47 +00:00
it ( "should allow verification when there is no existing DM" , function ( this : CryptoTestContext ) {
2023-01-19 15:19:07 +00:00
cy . bootstrapCrossSigning ( aliceCredentials ) ;
2022-11-30 13:03:47 +00:00
autoJoin ( this . bob ) ;
2022-11-25 13:54:06 +00:00
// we need to have a room with the other user present, so we can open the verification panel
2023-10-02 19:08:41 +00:00
createSharedRoomWithUser ( this . bob . getUserId ( ) ) ;
2022-11-30 13:03:47 +00:00
verify . call ( this ) ;
} ) ;
2022-11-25 13:54:06 +00:00
2023-09-05 11:11:10 +00:00
describe ( "event shields" , ( ) = > {
let testRoomId : string ;
2022-11-25 13:54:06 +00:00
2023-09-05 11:11:10 +00:00
beforeEach ( ( ) = > {
cy . bootstrapCrossSigning ( aliceCredentials ) ;
autoJoin ( bob ) ;
2022-11-25 13:54:06 +00:00
2023-09-05 11:11:10 +00:00
// create an encrypted room
2023-10-02 19:08:41 +00:00
createSharedRoomWithUser ( bob . getUserId ( ) )
2023-09-05 11:11:10 +00:00
. as ( "testRoomId" )
. then ( ( roomId ) = > {
testRoomId = roomId ;
2022-11-25 13:54:06 +00:00
2023-09-05 11:11:10 +00:00
// enable encryption
cy . getClient ( ) . then ( ( cli ) = > {
cli . sendStateEvent ( roomId , "m.room.encryption" , { algorithm : "m.megolm.v1.aes-sha2" } ) ;
} ) ;
2022-11-25 13:54:06 +00:00
} ) ;
2023-09-05 11:11:10 +00:00
} ) ;
it ( "should show the correct shield on e2e events" , function ( this : CryptoTestContext ) {
// Bob has a second, not cross-signed, device
let bobSecondDevice : MatrixClient ;
cy . loginBot ( homeserver , bob . getUserId ( ) , bob . __cypress_password , { } ) . then ( async ( data ) = > {
bobSecondDevice = data ;
2022-11-25 13:54:06 +00:00
} ) ;
2023-09-05 11:11:10 +00:00
/* Should show an error for a decryption failure */
2023-10-02 20:07:42 +00:00
cy . log ( "Testing decryption failure" ) ;
cy . wrap ( 0 )
. then ( ( ) = >
bob . sendEvent ( testRoomId , "m.room.encrypted" , {
algorithm : "m.megolm.v1.aes-sha2" ,
ciphertext : "the bird is in the hand" ,
} ) ,
)
. then ( ( resp ) = > cy . log ( ` Bob sent undecryptable event ${ resp . event_id } ` ) ) ;
2023-09-05 11:11:10 +00:00
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "Unable to decrypt message" )
. find ( ".mx_EventTile_e2eIcon" )
. should ( "have.class" , "mx_EventTile_e2eIcon_decryption_failure" )
. should ( "have.attr" , "aria-label" , "This message could not be decrypted" ) ;
/* Should show a red padlock for an unencrypted message in an e2e room */
2023-10-02 20:07:42 +00:00
cy . log ( "Testing unencrypted message" ) ;
2023-09-05 11:11:10 +00:00
cy . wrap ( 0 )
. then ( ( ) = >
bob . http . authedRequest < ISendEventResponse > (
// @ts-ignore-next this wants a Method instance, but that is hard to get to here
"PUT" ,
` /rooms/ ${ encodeURIComponent ( testRoomId ) } /send/m.room.message/test_txn_1 ` ,
undefined ,
{
msgtype : "m.text" ,
body : "test unencrypted" ,
} ,
) ,
)
. then ( ( resp ) = > cy . log ( ` Bob sent unencrypted event with event id ${ resp . event_id } ` ) ) ;
2022-11-25 13:54:06 +00:00
2023-09-05 11:11:10 +00:00
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test unencrypted" )
. find ( ".mx_EventTile_e2eIcon" )
. should ( "have.class" , "mx_EventTile_e2eIcon_warning" )
2023-09-29 07:49:26 +00:00
. should ( "have.attr" , "aria-label" , "Not encrypted" ) ;
2023-09-05 11:11:10 +00:00
/* Should show no padlock for an unverified user */
2023-10-02 20:07:42 +00:00
cy . log ( "Testing message from unverified user" ) ;
2022-11-25 13:54:06 +00:00
// bob sends a valid event
2023-09-05 11:11:10 +00:00
cy . wrap ( 0 )
. then ( ( ) = > bob . sendTextMessage ( testRoomId , "test encrypted 1" ) )
. then ( ( resp ) = > cy . log ( ` Bob sent message from primary device with event id ${ resp . event_id } ` ) ) ;
// the message should appear, decrypted, with no warning, but also no "verified"
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted 1" )
// no e2e icon
. should ( "not.have.descendants" , ".mx_EventTile_e2eIcon" ) ;
/* Now verify Bob */
2023-10-02 20:07:42 +00:00
cy . log ( "Verifying Bob" ) ;
2023-09-05 11:11:10 +00:00
verify . call ( this ) ;
/* Existing message should be updated when user is verified. */
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted 1" )
// still no e2e icon
. should ( "not.have.descendants" , ".mx_EventTile_e2eIcon" ) ;
/* should show no padlock, and be verified, for a message from a verified device */
2023-10-02 20:07:42 +00:00
cy . log ( "Testing message from verified device" ) ;
2023-09-05 11:11:10 +00:00
cy . wrap ( 0 )
. then ( ( ) = > bob . sendTextMessage ( testRoomId , "test encrypted 2" ) )
. then ( ( resp ) = > cy . log ( ` Bob sent second message from primary device with event id ${ resp . event_id } ` ) ) ;
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted 2" )
// no e2e icon
. should ( "not.have.descendants" , ".mx_EventTile_e2eIcon" ) ;
/* should show red padlock for a message from an unverified device */
2023-10-02 20:07:42 +00:00
cy . log ( "Testing message from unverified device of verified user" ) ;
2023-09-05 11:11:10 +00:00
cy . wrap ( 0 )
. then ( ( ) = > bobSecondDevice . sendTextMessage ( testRoomId , "test encrypted from unverified" ) )
. then ( ( resp ) = > cy . log ( ` Bob sent message from unverified device with event id ${ resp . event_id } ` ) ) ;
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted from unverified" )
2023-09-21 09:54:23 +00:00
. find ( ".mx_EventTile_e2eIcon" )
2023-09-05 11:11:10 +00:00
. should ( "have.class" , "mx_EventTile_e2eIcon_warning" )
2023-09-21 09:54:23 +00:00
. should ( "have.attr" , "aria-label" , "Encrypted by a device not verified by its owner." ) ;
2023-09-05 11:11:10 +00:00
/* Should show a grey padlock for a message from an unknown device */
2023-10-02 20:07:42 +00:00
cy . log ( "Testing message from unknown device" ) ;
2023-09-05 11:11:10 +00:00
2023-10-02 20:07:42 +00:00
// bob deletes his second device
2023-09-05 11:11:10 +00:00
cy . wrap ( 0 )
. then ( ( ) = > bobSecondDevice . logout ( true ) )
. then ( ( ) = > cy . log ( ` Bob logged out second device ` ) ) ;
2023-10-02 20:07:42 +00:00
// wait for the logout to propagate. Workaround for https://github.com/vector-im/element-web/issues/26263 by repeatedly closing and reopening Bob's user info.
function awaitOneDevice ( iterations = 1 ) {
let sessionCountText : string ;
cy . get ( ".mx_RightPanel" )
. within ( ( ) = > {
cy . findByRole ( "button" , { name : "Room members" } ) . click ( ) ;
cy . findByText ( "Bob" ) . click ( ) ;
return cy
. get ( ".mx_UserInfo_devices" )
. findByText ( " session" , { exact : false } )
. then ( ( data ) = > {
sessionCountText = data . text ( ) ;
} ) ;
} )
. then ( ( ) = > {
cy . log ( ` At ${ new Date ( ) . toISOString ( ) } : Bob has ' ${ sessionCountText } ' ` ) ;
// cf https://github.com/vector-im/element-web/issues/26279: Element-R uses the wrong text here
if ( sessionCountText != "1 session" && sessionCountText != "1 verified session" ) {
if ( iterations >= 10 ) {
throw new Error ( ` Bob still has ${ sessionCountText } after 10 iterations ` ) ;
}
awaitOneDevice ( iterations + 1 ) ;
}
} ) ;
}
awaitOneDevice ( ) ;
// close and reopen the room, to get the shield to update.
cy . viewRoomByName ( "Bob" ) ;
cy . viewRoomByName ( "TestRoom" ) ;
2023-09-21 09:54:23 +00:00
// some debate over whether this should have a red or a grey shield. Legacy crypto shows a grey shield,
// Rust crypto a red one.
2023-09-05 11:11:10 +00:00
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted from unverified" )
. find ( ".mx_EventTile_e2eIcon" )
2023-09-21 09:54:23 +00:00
//.should("have.class", "mx_EventTile_e2eIcon_normal")
2023-09-18 17:12:18 +00:00
. should ( "have.attr" , "aria-label" , "Encrypted by an unknown or deleted device." ) ;
2023-09-05 11:11:10 +00:00
} ) ;
it ( "Should show a grey padlock for a key restored from backup" , ( ) = > {
2023-09-18 21:34:24 +00:00
skipIfRustCrypto ( ) ; // requires key backup (https://github.com/vector-im/element-web/issues/24828)
2023-09-05 11:11:10 +00:00
enableKeyBackup ( ) ;
// bob sends a valid event
cy . wrap ( 0 )
. then ( ( ) = > bob . sendTextMessage ( testRoomId , "test encrypted 1" ) )
. then ( ( resp ) = > cy . log ( ` Bob sent message from primary device with event id ${ resp . event_id } ` ) ) ;
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted 1" )
// no e2e icon
. should ( "not.have.descendants" , ".mx_EventTile_e2eIcon" ) ;
2023-09-21 09:54:23 +00:00
/* log out, and back in */
2023-09-05 11:11:10 +00:00
logOutOfElement ( ) ;
cy . get < string > ( "@securityKey" ) . then ( ( securityKey ) = > {
logIntoElement ( homeserver . baseUrl , aliceCredentials . username , aliceCredentials . password , securityKey ) ;
} ) ;
/* go back to the test room and find Bob's message again */
cy . viewRoomById ( testRoomId ) ;
cy . get ( ".mx_EventTile_last" )
. should ( "contain" , "test encrypted 1" )
. find ( ".mx_EventTile_e2eIcon" )
. should ( "have.class" , "mx_EventTile_e2eIcon_normal" )
. should (
"have.attr" ,
"aria-label" ,
"The authenticity of this encrypted message can't be guaranteed on this device." ,
) ;
} ) ;
it ( "should show the correct shield on edited e2e events" , function ( this : CryptoTestContext ) {
// bob has a second, not cross-signed, device
cy . loginBot ( this . homeserver , this . bob . getUserId ( ) , this . bob . __cypress_password , { } ) . as ( "bobSecondDevice" ) ;
// verify Bob
verify . call ( this ) ;
cy . get < string > ( "@testRoomId" ) . then ( ( roomId ) = > {
// bob sends a valid event
cy . wrap ( this . bob . sendTextMessage ( roomId , "Hoo!" ) ) . as ( "testEvent" ) ;
// the message should appear, decrypted, with no warning
cy . get ( ".mx_EventTile_last .mx_EventTile_body" )
. within ( ( ) = > {
cy . findByText ( "Hoo!" ) ;
} )
. closest ( ".mx_EventTile" )
. should ( "not.have.descendants" , ".mx_EventTile_e2eIcon_warning" ) ;
// bob sends an edit to the first message with his unverified device
cy . get < MatrixClient > ( "@bobSecondDevice" ) . then ( ( bobSecondDevice ) = > {
cy . get < ISendEventResponse > ( "@testEvent" ) . then ( ( testEvent ) = > {
bobSecondDevice . sendMessage ( roomId , {
"m.new_content" : {
msgtype : "m.text" ,
body : "Haa!" ,
} ,
"m.relates_to" : {
rel_type : "m.replace" ,
event_id : testEvent.event_id ,
} ,
} ) ;
} ) ;
} ) ;
// the edit should have a warning
cy . contains ( ".mx_EventTile_body" , "Haa!" )
. closest ( ".mx_EventTile" )
. within ( ( ) = > {
cy . get ( ".mx_EventTile_e2eIcon_warning" ) . should ( "exist" ) ;
} ) ;
// a second edit from the verified device should be ok
2022-11-25 13:54:06 +00:00
cy . get < ISendEventResponse > ( "@testEvent" ) . then ( ( testEvent ) = > {
2023-09-05 11:11:10 +00:00
this . bob . sendMessage ( roomId , {
2022-11-25 13:54:06 +00:00
"m.new_content" : {
msgtype : "m.text" ,
2023-09-05 11:11:10 +00:00
body : "Hee!" ,
2022-11-25 13:54:06 +00:00
} ,
"m.relates_to" : {
rel_type : "m.replace" ,
event_id : testEvent.event_id ,
} ,
} ) ;
} ) ;
2023-09-05 11:11:10 +00:00
cy . get ( ".mx_EventTile_last .mx_EventTile_body" )
. within ( ( ) = > {
cy . findByText ( "Hee!" ) ;
} )
. closest ( ".mx_EventTile" )
. should ( "not.have.descendants" , ".mx_EventTile_e2eIcon_warning" ) ;
2022-11-25 13:54:06 +00:00
} ) ;
} ) ;
} ) ;
2022-05-30 13:26:48 +00:00
} ) ;