@@ -25,7 +25,8 @@ use matrix_sdk::{
2525} ;
2626use matrix_sdk_base:: crypto:: types:: events:: UtdCause ;
2727use matrix_sdk_common:: deserialized_responses:: {
28- ProcessedToDeviceEvent , UnableToDecryptReason :: MissingMegolmSession , WithheldCode ,
28+ DeviceLinkProblem , ProcessedToDeviceEvent , UnableToDecryptReason :: MissingMegolmSession ,
29+ VerificationLevel , VerificationState , WithheldCode ,
2930} ;
3031use matrix_sdk_ui:: {
3132 Timeline ,
@@ -553,6 +554,148 @@ async fn test_transitive_history_share_with_withhelds() -> Result<()> {
553554 Ok ( ( ) )
554555}
555556
557+ /// Test megolm session merging with history sharing
558+ ///
559+ /// 1. Alice and Bob share a room
560+ /// 2. Bob sends a message
561+ /// 3. Alice invites Charlie, sharing the history
562+ /// 4. Charlie can see Bob's message, but the sender is unauthenticated.
563+ /// 5. Bob sends another message (on the same session)
564+ /// 6. Charlie can now decrypt both of Bob's messages, with authenticated
565+ /// sender.
566+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 4 ) ]
567+ async fn test_history_sharing_session_merging ( ) -> Result < ( ) > {
568+ let alice_span = tracing:: info_span!( "alice" ) ;
569+ let bob_span = tracing:: info_span!( "bob" ) ;
570+ let charlie_span = tracing:: info_span!( "charlie" ) ;
571+
572+ let alice = create_encryption_enabled_client ( "alice" ) . instrument ( alice_span. clone ( ) ) . await ?;
573+ let bob = create_encryption_enabled_client ( "bob" ) . instrument ( bob_span. clone ( ) ) . await ?;
574+ let charlie =
575+ create_encryption_enabled_client ( "charlie" ) . instrument ( charlie_span. clone ( ) ) . await ?;
576+
577+ // 1. Alice creates a room, and enables encryption
578+ let alice_room = alice
579+ . create_room ( assign ! ( CreateRoomRequest :: new( ) , {
580+ preset: Some ( RoomPreset :: PublicChat ) ,
581+ } ) )
582+ . instrument ( alice_span. clone ( ) )
583+ . await ?;
584+ let alice_timeline = alice_room. timeline ( ) . await ?;
585+
586+ alice_room. enable_encryption ( ) . instrument ( alice_span. clone ( ) ) . await ?;
587+ info ! ( room_id = ?alice_room. room_id( ) , "Alice has created and enabled encryption in the room" ) ;
588+
589+ // ... and invites Bob to the room
590+ alice_room. invite_user_by_id ( bob. user_id ( ) . unwrap ( ) ) . instrument ( alice_span. clone ( ) ) . await ?;
591+
592+ // Bob joins
593+ bob. sync_once ( ) . instrument ( bob_span. clone ( ) ) . await ?;
594+
595+ let bob_room = bob
596+ . join_room_by_id ( alice_room. room_id ( ) )
597+ . instrument ( bob_span. clone ( ) )
598+ . await
599+ . expect ( "Bob should be able to accept the invitation from Alice" ) ;
600+
601+ // 2. Bob sends a message, which Alice should receive
602+ let bob_send_test_event = async |event_content : & str | {
603+ let bob_event_id = bob_room
604+ . send ( RoomMessageEventContent :: text_plain ( event_content) )
605+ . into_future ( )
606+ . instrument ( bob_span. clone ( ) )
607+ . await
608+ . expect ( "We should be able to send a message to the room" )
609+ . event_id ;
610+
611+ alice
612+ . sync_once ( )
613+ . instrument ( alice_span. clone ( ) )
614+ . await
615+ . expect ( "Alice should be able to sync" ) ;
616+
617+ assert_event_received ( & alice_timeline, & bob_event_id, event_content) . await ;
618+
619+ bob_event_id
620+ } ;
621+
622+ let event_id_1 = bob_send_test_event ( "Event 1" ) . await ;
623+
624+ // 3. Alice invites Charlie.
625+ alice_room. invite_user_by_id ( charlie. user_id ( ) . unwrap ( ) ) . instrument ( alice_span. clone ( ) ) . await ?;
626+
627+ // Workaround for https://github.com/matrix-org/matrix-rust-sdk/issues/5770: Charlie needs a copy of
628+ // Alice's identity.
629+ charlie
630+ . encryption ( )
631+ . request_user_identity ( alice. user_id ( ) . unwrap ( ) )
632+ . instrument ( charlie_span. clone ( ) )
633+ . await ?;
634+
635+ charlie. sync_once ( ) . instrument ( charlie_span. clone ( ) ) . await ?;
636+ let charlie_room = charlie
637+ . join_room_by_id ( alice_room. room_id ( ) )
638+ . instrument ( charlie_span. clone ( ) )
639+ . await
640+ . expect ( "Charlie should be able to accept the invitation from Alice" ) ;
641+
642+ // 4. Charlie can see Bob's message, but the sender is unauthenticated.
643+ let charlie_timeline = charlie_room. timeline ( ) . await ?;
644+ charlie. sync_once ( ) . instrument ( charlie_span. clone ( ) ) . await ?;
645+ let received_event = assert_event_received ( & charlie_timeline, & event_id_1, "Event 1" ) . await ;
646+ assert_eq ! (
647+ received_event
648+ . as_event( )
649+ . unwrap( )
650+ . encryption_info( )
651+ . expect( "Received event should be encrypted" )
652+ . verification_state,
653+ VerificationState :: Unverified ( VerificationLevel :: None ( DeviceLinkProblem :: InsecureSource ) )
654+ ) ;
655+
656+ // 5. Bob sends another message (on the same session)
657+ bob. sync_once ( ) . instrument ( bob_span. clone ( ) ) . await ?;
658+ // Sanity: make sure Bob knows that Charlie has joined
659+ bob_room
660+ . get_member_no_sync ( charlie. user_id ( ) . unwrap ( ) )
661+ . instrument ( bob_span. clone ( ) )
662+ . await ?
663+ . expect ( "Bob should see Charlie in the room" ) ;
664+ let event_id_2 = bob_send_test_event ( "Event 2" ) . await ;
665+
666+ // 6. Charlie can now decrypt both of Bob's messages, with authenticated sender
667+ let mut charlie_room_stream = charlie. encryption ( ) . room_keys_received_stream ( ) . await . unwrap ( ) ;
668+ charlie. sync_once ( ) . instrument ( charlie_span. clone ( ) ) . await ?;
669+
670+ // Make sure we're decrypting with the newly-received keys.
671+ assert_next_with_timeout ! ( & mut charlie_room_stream) . expect ( "charlie should receive room keys" ) ;
672+
673+ let received_event = assert_event_received ( & charlie_timeline, & event_id_2, "Event 2" ) . await ;
674+ assert_eq ! (
675+ received_event
676+ . as_event( )
677+ . unwrap( )
678+ . encryption_info( )
679+ . expect( "Received event should be encrypted" )
680+ . verification_state,
681+ VerificationState :: Unverified ( VerificationLevel :: UnverifiedIdentity )
682+ ) ;
683+
684+ // The earlier event should now have a better verification status.
685+ let received_event = assert_event_received ( & charlie_timeline, & event_id_1, "Event 1" ) . await ;
686+ assert_eq ! (
687+ received_event
688+ . as_event( )
689+ . unwrap( )
690+ . encryption_info( )
691+ . expect( "Received event should be encrypted" )
692+ . verification_state,
693+ VerificationState :: Unverified ( VerificationLevel :: UnverifiedIdentity )
694+ ) ;
695+
696+ Ok ( ( ) )
697+ }
698+
556699async fn create_encryption_enabled_client ( username : & str ) -> Result < SyncTokenAwareClient > {
557700 let encryption_settings =
558701 EncryptionSettings { auto_enable_cross_signing : true , ..Default :: default ( ) } ;
@@ -624,7 +767,11 @@ async fn wait_for_timeline_event(
624767 * Wait for the given event to arrive in the timeline, and assert that its
625768 * content matches that given.
626769 */
627- async fn assert_event_received ( timeline : & Timeline , event_id : & EventId , expected_content : & str ) {
770+ async fn assert_event_received (
771+ timeline : & Timeline ,
772+ event_id : & EventId ,
773+ expected_content : & str ,
774+ ) -> Arc < TimelineItem > {
628775 let timeline_item = wait_for_timeline_event ( timeline, event_id) . await . unwrap_or_else ( || {
629776 panic ! ( "Timeout waiting for event {event_id} with content {expected_content} to arrive" )
630777 } ) ;
@@ -639,6 +786,8 @@ async fn assert_event_received(timeline: &Timeline, event_id: &EventId, expected
639786 expected_content,
640787 "The decrypted event should match the message Bob has sent"
641788 ) ;
789+
790+ timeline_item
642791}
643792
644793/**
0 commit comments