tests: Assert room key is rotated when a member leaves the room
Signed-off-by: Skye Elliot <actuallyori@gmail.com>
This commit is contained in:
@@ -1127,6 +1127,115 @@ async fn test_history_share_on_invite_respects_history_visibility() -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that when a user leaves a room that uses history sharing, the room key
|
||||
/// is rotated so they cannot decrypt future messages.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||
async fn test_history_share_on_invite_room_key_rotation() -> Result<()> {
|
||||
let alice_span = tracing::info_span!("alice");
|
||||
let bob_span = tracing::info_span!("bob");
|
||||
let charlie_span = tracing::info_span!("charlie");
|
||||
|
||||
let alice =
|
||||
create_encryption_enabled_client("alice", false).instrument(alice_span.clone()).await?;
|
||||
let bob = create_encryption_enabled_client("bob", false).instrument(bob_span.clone()).await?;
|
||||
let charlie = create_encryption_enabled_client("charlie", false)
|
||||
.await
|
||||
.expect("Failed to create Charlie's client");
|
||||
|
||||
// 1. Alice creates a room with `shared` history visibility and invites Bob.
|
||||
let alice_room = alice
|
||||
.create_room(assign!(CreateRoomRequest::new(), {
|
||||
preset: Some(RoomPreset::PublicChat),
|
||||
}))
|
||||
.instrument(alice_span.clone())
|
||||
.await?;
|
||||
alice_room.enable_encryption().instrument(alice_span.clone()).await?;
|
||||
|
||||
alice_room.invite_user_by_id(bob.user_id().unwrap()).instrument(alice_span.clone()).await?;
|
||||
|
||||
bob.sync_once().instrument(bob_span.clone()).await?;
|
||||
let bob_room = bob.join_room_by_id(alice_room.room_id()).instrument(bob_span.clone()).await?;
|
||||
|
||||
alice.sync_once().instrument(alice_span.clone()).await?;
|
||||
|
||||
// 2. Bob sends M1, which Charlie should be able to read later as Alice will
|
||||
// send them a key bundle.
|
||||
let event_id_a = bob_room
|
||||
.send(RoomMessageEventContent::text_plain("Charlie is cool!"))
|
||||
.into_future()
|
||||
.instrument(bob_span.clone())
|
||||
.await?
|
||||
.response
|
||||
.event_id;
|
||||
|
||||
// Store the session ID for later comparison.
|
||||
let event_m1 = bob_room.event(&event_id_a, None).instrument(bob_span.clone()).await?;
|
||||
let event_m1_session_id = event_m1
|
||||
.encryption_info()
|
||||
.and_then(|info| info.session_id())
|
||||
.expect("Bob should be able to check the session ID of event M1");
|
||||
|
||||
// 3. Alice invites Charlie; Charlie joins and receives the keys for M1.
|
||||
alice.sync_once().instrument(alice_span.clone()).await?;
|
||||
alice_room.invite_user_by_id(charlie.user_id().unwrap()).instrument(alice_span.clone()).await?;
|
||||
|
||||
let sync_response = charlie.sync_once().instrument(charlie_span.clone()).await?;
|
||||
assert_received_room_key_bundle(sync_response);
|
||||
|
||||
let charlie_room =
|
||||
charlie.join_room_by_id(alice_room.room_id()).instrument(charlie_span.clone()).await?;
|
||||
|
||||
charlie.sync_once().instrument(charlie_span.clone()).await?;
|
||||
|
||||
// Sanity check: Charlie can decrypt message M1 via the bundle.
|
||||
let event_a = charlie_room.event(&event_id_a, None).instrument(charlie_span.clone()).await?;
|
||||
assert!(
|
||||
event_a.encryption_info().is_some(),
|
||||
"Charlie should be able to decrypt message M1 via the key bundle"
|
||||
);
|
||||
|
||||
// 4. Charlie leaves the room.
|
||||
charlie_room.leave().instrument(charlie_span.clone()).await?;
|
||||
|
||||
// Bob syncs to learn about Charlie's departure, which should trigger key
|
||||
// rotation.
|
||||
bob.sync_once().instrument(bob_span.clone()).await?;
|
||||
|
||||
// 5. Bob sends M2. Because key rotation should have been performed, this should
|
||||
// be using a fresh session that hasn't been shared with Charlie.
|
||||
let event_id_b = bob_room
|
||||
.send(RoomMessageEventContent::text_plain("Charlie is mean!"))
|
||||
.into_future()
|
||||
.instrument(bob_span.clone())
|
||||
.await?
|
||||
.response
|
||||
.event_id;
|
||||
|
||||
// Ensure the two session IDs of M1 and M2 are different
|
||||
let event_b = bob_room.event(&event_id_b, None).instrument(bob_span.clone()).await?;
|
||||
let event_m2_session_id = event_b
|
||||
.encryption_info()
|
||||
.and_then(|info| info.session_id())
|
||||
.expect("Bob should be able to check the session ID of event M2");
|
||||
|
||||
assert_ne!(event_m1_session_id, event_m2_session_id, "Session was not rotated");
|
||||
|
||||
// 6. Charlie rejoins the room via ID.
|
||||
charlie.sync_once().instrument(charlie_span.clone()).await?;
|
||||
let charlie_room =
|
||||
charlie.join_room_by_id(alice_room.room_id()).instrument(charlie_span.clone()).await?;
|
||||
|
||||
// 7. Charlie attempts to decrypt M2. He should not be able to, because the
|
||||
// session was rotated after he left the room.
|
||||
let event_b = charlie_room.event(&event_id_b, None).instrument(charlie_span.clone()).await?;
|
||||
assert!(
|
||||
event_b.encryption_info().is_none(),
|
||||
"Charlie should not be able to decrypt message M2 after rejoining"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new encryption-enabled client with the given username and
|
||||
/// settings.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user