@@ -30,7 +30,8 @@ import * as olmlib from "../../../../src/crypto/olmlib";
3030import { TypedEventEmitter } from '../../../../src/models/typed-event-emitter' ;
3131import { ClientEvent , MatrixClient , RoomMember } from '../../../../src' ;
3232import { DeviceInfo , IDevice } from '../../../../src/crypto/deviceinfo' ;
33- import { DeviceTrustLevel } from '../../../../src/crypto/CrossSigning' ;
33+ import { DeviceTrustLevel , UserTrustLevel } from '../../../../src/crypto/CrossSigning' ;
34+ import { resetCrossSigningKeys } from "../crypto-utils" ;
3435
3536const MegolmDecryption = algorithms . DECRYPTION_CLASSES [ 'm.megolm.v1.aes-sha2' ] ;
3637const MegolmEncryption = algorithms . ENCRYPTION_CLASSES [ 'm.megolm.v1.aes-sha2' ] ;
@@ -344,6 +345,10 @@ describe("MegolmDecryption", function() {
344345 } ,
345346 } ) ) ;
346347
348+ mockCrypto . checkUserTrust . mockReturnValue ( {
349+ isVerified : ( ) => false ,
350+ } as UserTrustLevel ) ;
351+
347352 mockCrypto . checkDeviceTrust . mockReturnValue ( {
348353 isVerified : ( ) => false ,
349354 } as DeviceTrustLevel ) ;
@@ -577,6 +582,120 @@ describe("MegolmDecryption", function() {
577582 bobClient2 . stopClient ( ) ;
578583 } ) ;
579584
585+ it ( "always blocks unverified devices of verified users" , async function ( ) {
586+ const keys = { } ;
587+ function getCrossSigningKey ( keyType : string ) {
588+ return keys [ keyType ] ;
589+ }
590+
591+ function saveCrossSigningKeys ( k : Record < string , Uint8Array > ) {
592+ Object . assign ( keys , k ) ;
593+ }
594+
595+ const aliceClient = ( new TestClient (
596+ "@alice:example.com" , "alicedevice" ,
597+ undefined , undefined ,
598+ { cryptoCallbacks : { getCrossSigningKey, saveCrossSigningKeys } } ,
599+ ) ) . client ;
600+ const bobClient1 = ( new TestClient (
601+ "@bob:example.com" , "bobdevice1" ,
602+ ) ) . client ;
603+ await Promise . all ( [
604+ aliceClient . initCrypto ( ) ,
605+ bobClient1 . initCrypto ( ) ,
606+ ] ) ;
607+ const aliceDevice = aliceClient . crypto . olmDevice ;
608+ const bobDevice1 = bobClient1 . crypto . olmDevice ;
609+
610+ aliceClient . uploadDeviceSigningKeys = async ( ) => ( { } ) ;
611+ aliceClient . uploadKeySignatures = async ( ) => ( { failures : { } } ) ;
612+ // set Alice's cross-signing key
613+ await resetCrossSigningKeys ( aliceClient ) ;
614+ // Alice downloads Bob's device key
615+ aliceClient . crypto . deviceList . storeCrossSigningForUser ( "@bob:example.com" , {
616+ keys : {
617+ master : {
618+ user_id : "@bob:example.com" ,
619+ usage : [ "master" ] ,
620+ keys : {
621+ "ed25519:bobs+master+pubkey" : "bobs+master+pubkey" ,
622+ } ,
623+ } ,
624+ } ,
625+ firstUse : false ,
626+ crossSigningVerifiedBefore : false ,
627+ } ) ;
628+ await aliceClient . setDeviceVerified ( "@bob:example.com" , "bobs+master+pubkey" , true ) ;
629+
630+ const encryptionCfg = {
631+ "algorithm" : "m.megolm.v1.aes-sha2" ,
632+ } ;
633+ const roomId = "!someroom" ;
634+ const room = new Room ( roomId , aliceClient , "@alice:example.com" , { } ) ;
635+ const bobMember = new RoomMember ( roomId , "@bob:example.com" ) ;
636+ room . getEncryptionTargetMembers = async function ( ) {
637+ return [ bobMember ] ;
638+ } ;
639+ room . setBlacklistUnverifiedDevices ( false ) ;
640+ aliceClient . store . storeRoom ( room ) ;
641+ await aliceClient . setRoomEncryption ( roomId , encryptionCfg ) ;
642+
643+ const BOB_DEVICES = {
644+ bobdevice1 : {
645+ user_id : "@bob:example.com" ,
646+ device_id : "bobdevice1" ,
647+ algorithms : [ olmlib . OLM_ALGORITHM , olmlib . MEGOLM_ALGORITHM ] ,
648+ keys : {
649+ "ed25519:Dynabook" : bobDevice1 . deviceEd25519Key ,
650+ "curve25519:Dynabook" : bobDevice1 . deviceCurve25519Key ,
651+ } ,
652+ verified : 0 ,
653+ known : true ,
654+ } ,
655+ } ;
656+
657+ aliceClient . crypto . deviceList . storeDevicesForUser (
658+ "@bob:example.com" , BOB_DEVICES ,
659+ ) ;
660+ aliceClient . crypto . deviceList . downloadKeys = async function ( userIds ) {
661+ return this . getDevicesFromStore ( userIds ) ;
662+ } ;
663+
664+ aliceClient . sendToDevice = jest . fn ( ) . mockResolvedValue ( { } ) ;
665+
666+ const event = new MatrixEvent ( {
667+ type : "m.room.message" ,
668+ sender : "@alice:example.com" ,
669+ room_id : roomId ,
670+ event_id : "$event" ,
671+ content : {
672+ msgtype : "m.text" ,
673+ body : "secret" ,
674+ } ,
675+ } ) ;
676+ await aliceClient . crypto . encryptEvent ( event , room ) ;
677+
678+ expect ( aliceClient . sendToDevice ) . toHaveBeenCalled ( ) ;
679+ const [ msgtype , contentMap ] = mocked ( aliceClient . sendToDevice ) . mock . calls [ 0 ] ;
680+ expect ( msgtype ) . toMatch ( / ^ ( o r g .m a t r i x | m ) .r o o m _ k e y .w i t h h e l d $ / ) ;
681+ delete contentMap [ "@bob:example.com" ] . bobdevice1 . session_id ;
682+ expect ( contentMap ) . toStrictEqual ( {
683+ '@bob:example.com' : {
684+ bobdevice1 : {
685+ algorithm : "m.megolm.v1.aes-sha2" ,
686+ room_id : roomId ,
687+ code : 'm.unverified' ,
688+ reason :
689+ 'The sender has disabled encrypting to unverified devices.' ,
690+ sender_key : aliceDevice . deviceCurve25519Key ,
691+ } ,
692+ } ,
693+ } ) ;
694+
695+ aliceClient . stopClient ( ) ;
696+ bobClient1 . stopClient ( ) ;
697+ } ) ;
698+
580699 it ( "notifies devices when unable to create olm session" , async function ( ) {
581700 const aliceClient = ( new TestClient (
582701 "@alice:example.com" , "alicedevice" ,
0 commit comments