From 36428564fc61d4d9f5db9ce0954ea9964e988c47 Mon Sep 17 00:00:00 2001 From: Skye Elliot Date: Wed, 20 Aug 2025 12:45:04 +0100 Subject: [PATCH 1/3] feat(sdk): Poll encryption state on sync event for up to 3 seconds. --- crates/matrix-sdk/CHANGELOG.md | 4 +++ crates/matrix-sdk/src/room/mod.rs | 48 +++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index 8823f78e3..71a66492e 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file. ### Features +- `Room::enable_encryption` and `Room::enable_encryption_with_state_event_encryption` will poll + the encryption state for up to 3 seconds, rather than checking once after a single sync has + completed. + ([#5559](https://github.com/matrix-org/matrix-rust-sdk/pull/5559)) - Add `Room::enable_encryption_with_state` to enable E2E encryption with encrypted state event support, gated behind the `experimental-encrypted-state-events` feature. ([#5557](https://github.com/matrix-org/matrix-rust-sdk/pull/5557)) diff --git a/crates/matrix-sdk/src/room/mod.rs b/crates/matrix-sdk/src/room/mod.rs index 49def01dd..3c99d7717 100644 --- a/crates/matrix-sdk/src/room/mod.rs +++ b/crates/matrix-sdk/src/room/mod.rs @@ -1908,29 +1908,47 @@ impl Room { } self.send_state_event(content).await?; + // Spin on the sync beat event, since the first sync we receive might not + // include the encryption event. + // // TODO do we want to return an error here if we time out? This // could be quite useful if someone wants to enable encryption and // send a message right after it's enabled. - _ = timeout(self.client.inner.sync_beat.listen(), SYNC_WAIT_TIME).await; + let res = timeout( + async { + loop { + // Listen for sync events, then check if the encryption state has been set. + self.client.inner.sync_beat.listen().await; + let _sync_lock = self.client.base_client().sync_lock().lock().await; + if self.inner.encryption_state().is_encrypted() { + break; + } + } + }, + SYNC_WAIT_TIME, + ) + .await; - // If after waiting for a sync, we don't have the encryption state we expect, - // assume the local encryption state is incorrect; this will cause - // the SDK to re-request it later for confirmation, instead of + // If encryption was enabled, return. + if res.is_ok() { + debug!("room successfully marked as encrypted"); + return Ok(()); + } + + // If after waiting for multiple syncs, we don't have the encryption state we + // expect, assume the local encryption state is incorrect; this will + // cause the SDK to re-request it later for confirmation, instead of // assuming it's sync'd and correct (and not encrypted). let _sync_lock = self.client.base_client().sync_lock().lock().await; - if !self.inner.encryption_state().is_encrypted() { - debug!("still not marked as encrypted, marking encryption state as missing"); + debug!("still not marked as encrypted, marking encryption state as missing"); - let mut room_info = self.clone_info(); - room_info.mark_encryption_state_missing(); - let mut changes = StateChanges::default(); - changes.add_room(room_info.clone()); + let mut room_info = self.clone_info(); + room_info.mark_encryption_state_missing(); + let mut changes = StateChanges::default(); + changes.add_room(room_info.clone()); - self.client.state_store().save_changes(&changes).await?; - self.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty()); - } else { - debug!("room successfully marked as encrypted"); - } + self.client.state_store().save_changes(&changes).await?; + self.set_room_info(room_info, RoomInfoNotableUpdateReasons::empty()); } Ok(()) From 7880ec5b018f9a6be9aa1b69d3f7c8d1810c2cc9 Mon Sep 17 00:00:00 2001 From: Skye Elliot Date: Wed, 20 Aug 2025 13:56:05 +0100 Subject: [PATCH 2/3] fix(sdk): Return from poll when encryption state known Fixes Room::enable_encryption_inner to break out of polling the encryption state when it becomes known, rather than just encrypted. --- crates/matrix-sdk/src/room/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk/src/room/mod.rs b/crates/matrix-sdk/src/room/mod.rs index 3c99d7717..381127ff0 100644 --- a/crates/matrix-sdk/src/room/mod.rs +++ b/crates/matrix-sdk/src/room/mod.rs @@ -1917,10 +1917,10 @@ impl Room { let res = timeout( async { loop { - // Listen for sync events, then check if the encryption state has been set. + // Listen for sync events, then check if the encryption state is known. self.client.inner.sync_beat.listen().await; let _sync_lock = self.client.base_client().sync_lock().lock().await; - if self.inner.encryption_state().is_encrypted() { + if !self.inner.encryption_state().is_unknown() { break; } } @@ -1930,7 +1930,8 @@ impl Room { .await; // If encryption was enabled, return. - if res.is_ok() { + let _sync_lock = self.client.base_client().sync_lock().lock().await; + if res.is_ok() && self.inner.encryption_state().is_encrypted() { debug!("room successfully marked as encrypted"); return Ok(()); } @@ -1939,7 +1940,6 @@ impl Room { // expect, assume the local encryption state is incorrect; this will // cause the SDK to re-request it later for confirmation, instead of // assuming it's sync'd and correct (and not encrypted). - let _sync_lock = self.client.base_client().sync_lock().lock().await; debug!("still not marked as encrypted, marking encryption state as missing"); let mut room_info = self.clone_info(); From 24502d270654ce76389541d362bc73f369ff5e6e Mon Sep 17 00:00:00 2001 From: Skye Elliot Date: Thu, 21 Aug 2025 15:37:55 +0100 Subject: [PATCH 3/3] fix(sdk): Check correct encryption state for encrypted state events --- crates/matrix-sdk/src/room/mod.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/matrix-sdk/src/room/mod.rs b/crates/matrix-sdk/src/room/mod.rs index 381127ff0..70fd781ac 100644 --- a/crates/matrix-sdk/src/room/mod.rs +++ b/crates/matrix-sdk/src/room/mod.rs @@ -1929,13 +1929,28 @@ impl Room { ) .await; - // If encryption was enabled, return. let _sync_lock = self.client.base_client().sync_lock().lock().await; + + // If encryption was enabled, return. + #[cfg(not(feature = "experimental-encrypted-state-events"))] if res.is_ok() && self.inner.encryption_state().is_encrypted() { debug!("room successfully marked as encrypted"); return Ok(()); } + // If encryption with state event encryption was enabled, return. + #[cfg(feature = "experimental-encrypted-state-events")] + if res.is_ok() && { + if encrypted_state_events { + self.inner.encryption_state().is_state_encrypted() + } else { + self.inner.encryption_state().is_encrypted() + } + } { + debug!("room successfully marked as encrypted"); + return Ok(()); + } + // If after waiting for multiple syncs, we don't have the encryption state we // expect, assume the local encryption state is incorrect; this will // cause the SDK to re-request it later for confirmation, instead of