From 818d7502f2bebea432eab76ea203f81f77ca7d62 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 4 Sep 2024 14:53:28 +0100 Subject: [PATCH 1/6] crypto: expose `InboundGroupSession.sender_data` We need write access to this in the integration tests --- crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs b/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs index cf17841a093..ac4d269fed1 100644 --- a/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs +++ b/crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs @@ -127,7 +127,7 @@ pub struct InboundGroupSession { /// the session, or, if we can use that device information to find the /// sender's cross-signing identity, holds the user ID and cross-signing /// key. - pub(crate) sender_data: SenderData, + pub sender_data: SenderData, /// The Room this GroupSession belongs to pub room_id: OwnedRoomId, From 9b4c58aa4b63d0763ba625cd265486ed5eb638f4 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 4 Sep 2024 10:38:25 +0100 Subject: [PATCH 2/6] crypto: fix memorystore groupsession batch query If the previous session is removed from the list, we should still be able to continue iterating through the *rest* of the list. --- .../matrix-sdk-crypto/src/store/integration_tests.rs | 5 +++++ crates/matrix-sdk-crypto/src/store/memorystore.rs | 11 +++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/store/integration_tests.rs b/crates/matrix-sdk-crypto/src/store/integration_tests.rs index d696751bd5d..24098de3f0e 100644 --- a/crates/matrix-sdk-crypto/src/store/integration_tests.rs +++ b/crates/matrix-sdk-crypto/src/store/integration_tests.rs @@ -648,6 +648,11 @@ macro_rules! cryptostore_integration_tests { // Check that there are exactly two results in the batch assert_eq!(sessions_1_k_batch.len(), 2); + // Modify one of the results, to check that that doesn't break iteration + let mut last_session = last_session.clone(); + last_session.sender_data = SenderData::unknown(); + store.save_inbound_group_sessions(vec![last_session], None).await.unwrap(); + previous_last_session_id = Some(last_session.session_id().to_owned()); sessions_1_k.append(&mut sessions_1_k_batch); } diff --git a/crates/matrix-sdk-crypto/src/store/memorystore.rs b/crates/matrix-sdk-crypto/src/store/memorystore.rs index 1a8b00ffca1..b773d819d94 100644 --- a/crates/matrix-sdk-crypto/src/store/memorystore.rs +++ b/crates/matrix-sdk-crypto/src/store/memorystore.rs @@ -408,13 +408,12 @@ impl CryptoStore for MemoryStore { match after_session_id { None => 0, Some(id) => { - let idx = sessions + // We're looking for the first session with a session ID strictly after `id`; if + // there are none, the end of the array. + sessions .iter() - .position(|session| session.session_id() == id) - .map(|idx| idx + 1); - - // If `after_session_id` was not found in the array, go to the end of the array - idx.unwrap_or(sessions.len()) + .position(|session| session.session_id() > id.as_str()) + .unwrap_or(sessions.len()) } } }; From ce79ba17d4259ba443c36a21f4170c8319f84587 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 30 Aug 2024 16:19:51 +0100 Subject: [PATCH 3/6] crypto: Expose sender_data_finder module as pub(crate) This module has a number of useful types (in particular, error types). Rather than addding even more types to the top level module, let's export the `sender_data_finder` module as a whole. --- crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs | 3 +-- crates/matrix-sdk-crypto/src/olm/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs b/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs index 55eee23d74b..54f6b661b03 100644 --- a/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs +++ b/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; mod inbound; mod outbound; mod sender_data; -mod sender_data_finder; +pub(crate) mod sender_data_finder; pub use inbound::{InboundGroupSession, PickledInboundGroupSession}; pub(crate) use outbound::ShareState; @@ -26,7 +26,6 @@ pub use outbound::{ EncryptionSettings, OutboundGroupSession, PickledOutboundGroupSession, ShareInfo, }; pub use sender_data::{KnownSenderData, SenderData, SenderDataType}; -pub(crate) use sender_data_finder::SenderDataFinder; use thiserror::Error; pub use vodozemac::megolm::{ExportedSessionKey, SessionKey}; use vodozemac::{megolm::SessionKeyDecodeError, Curve25519PublicKey}; diff --git a/crates/matrix-sdk-crypto/src/olm/mod.rs b/crates/matrix-sdk-crypto/src/olm/mod.rs index 2f47d65eea6..c4c01d3d430 100644 --- a/crates/matrix-sdk-crypto/src/olm/mod.rs +++ b/crates/matrix-sdk-crypto/src/olm/mod.rs @@ -25,12 +25,15 @@ mod utility; pub use account::{Account, OlmMessageHash, PickledAccount, StaticAccountData}; pub(crate) use account::{OlmDecryptionInfo, SessionType}; +pub(crate) use group_sessions::{ + sender_data_finder::{self, SenderDataFinder}, + ShareState, +}; pub use group_sessions::{ BackedUpRoomKey, EncryptionSettings, ExportedRoomKey, InboundGroupSession, KnownSenderData, OutboundGroupSession, PickledInboundGroupSession, PickledOutboundGroupSession, SenderData, SenderDataType, SessionCreationError, SessionExportError, SessionKey, ShareInfo, }; -pub(crate) use group_sessions::{SenderDataFinder, ShareState}; pub use session::{PickledSession, Session}; pub use signing::{CrossSigningStatus, PickledCrossSigningIdentity, PrivateCrossSigningIdentity}; pub(crate) use utility::{SignedJsonObject, VerifyJson}; From fac64b6599cffc4316c1fd2fe4fc40aa97bb380c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 30 Aug 2024 16:50:28 +0100 Subject: [PATCH 4/6] crypto: update sender data on `/keys/query` responses When we receive an `/keys/query` response, look for existing inboundgroupsessions created by updated devices, and see if we can update any of their senderdata settings. --- .../src/identities/manager.rs | 218 +++++++++++++++++- .../src/store/integration_tests.rs | 3 +- 2 files changed, 219 insertions(+), 2 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/identities/manager.rs b/crates/matrix-sdk-crypto/src/identities/manager.rs index ba701ed0288..f3e7c40a9e8 100644 --- a/crates/matrix-sdk-crypto/src/identities/manager.rs +++ b/crates/matrix-sdk-crypto/src/identities/manager.rs @@ -32,7 +32,7 @@ use tracing::{debug, enabled, info, instrument, trace, warn, Level}; use crate::{ error::OlmResult, identities::{DeviceData, OtherUserIdentityData, OwnUserIdentityData, UserIdentityData}, - olm::PrivateCrossSigningIdentity, + olm::{InboundGroupSession, PrivateCrossSigningIdentity, SenderDataFinder, SenderDataType}, requests::KeysQueryRequest, store::{ caches::SequenceNumber, Changes, DeviceChanges, IdentityChanges, KeyQueryManager, @@ -162,6 +162,24 @@ impl IdentityManager { self.store.save_changes(changes).await?; + // Update the sender data on any existing inbound group sessions based on the + // changes in this response. + // + // `update_sender_data_from_device_changes` relies on being able to look up the + // user identities from the store, so this has to happen *after* the + // changes from `handle_cross_signing_keys` are saved. + // + // Note: it might be possible for this to race against session creation. If a + // new session is received at the same time as a `/keys/query` response is being + // processed, it could be saved without up-to-date sender data, but it might be + // saved too late for it to be picked up by + // `update_sender_data_from_device_changes`. However, this should be rare, + // since, in general, /sync responses which might create a new session + // are not processed at the same time as /keys/query responses (assuming + // that the application does not call `OlmMachine::receive_sync_changes` + // at the same time as `OlmMachine::mark_request_as_sent`). + self.update_sender_data_from_device_changes(&devices).await?; + // if this request is one of those we expected to be in flight, pass the // sequence number back to the store so that it can mark devices up to // date @@ -1019,6 +1037,113 @@ impl IdentityManager { _ => Ok(None), } } + + /// Given a list of changed devices, update any [`InboundGroupSession`]s + /// which were sent from those devices and which do not have complete + /// sender data. + async fn update_sender_data_from_device_changes( + &self, + device_changes: &DeviceChanges, + ) -> Result<(), CryptoStoreError> { + for device in device_changes.new.iter().chain(device_changes.changed.iter()) { + // 1. Look for InboundGroupSessions from the device whose sender_data is + // UnknownDevice. For such sessions, we now have the device, and can update + // the sender_data accordingly. + // + // In theory, we only need to do this for new devices. In practice, I'm a bit + // worried about races leading us to getting stuck in the + // UnknownDevice state, so we'll paper over that by doing this check + // on device updates too. + self.update_sender_data_for_sessions_for_device(device, SenderDataType::UnknownDevice) + .await?; + + // 2. If, and only if, the device is now correctly cross-signed (ie, + // device.is_cross_signed_by_owner() is true, and we have the master + // cross-signing key for the owner), look for InboundGroupSessions from the + // device whose sender_data is DeviceInfo. We can also update the sender_data + // for these sessions. + // + // In theory, we can skip a couple of steps of the SenderDataFinder algorithm, + // because we're doing the cross-signing check here. In practice, + // it's *way* easier just to use the same logic. + let device_owner_identity = self.store.get_user_identity(device.user_id()).await?; + if device_owner_identity.is_some_and(|id| device.is_cross_signed_by_owner(&id)) { + self.update_sender_data_for_sessions_for_device(device, SenderDataType::DeviceInfo) + .await?; + } + } + + Ok(()) + } + + /// Given a device, look for [`InboundGroupSession`]s whose sender data is + /// in the given state, and update it. + #[instrument(skip(self))] + async fn update_sender_data_for_sessions_for_device( + &self, + device: &DeviceData, + sender_data_type: SenderDataType, + ) -> Result<(), CryptoStoreError> { + const IGS_BATCH_SIZE: usize = 50; + + let Some(curve_key) = device.curve25519_key() else { return Ok(()) }; + + let mut last_session_id: Option = None; + loop { + let mut sessions = self + .store + .get_inbound_group_sessions_for_device_batch( + curve_key, + sender_data_type, + last_session_id, + IGS_BATCH_SIZE, + ) + .await?; + + if sessions.is_empty() { + // end of the session list + return Ok(()); + } + + last_session_id = None; + for session in &mut sessions { + last_session_id = Some(session.session_id().to_owned()); + self.update_sender_data_for_session(session, device).await?; + } + self.store.save_inbound_group_sessions(&sessions).await?; + } + } + + /// Update the sender data on the given inbound group session, using the + /// given device data. + #[instrument(skip(self, device, session), fields(session_id = session.session_id()))] + async fn update_sender_data_for_session( + &self, + session: &mut InboundGroupSession, + device: &DeviceData, + ) -> Result<(), CryptoStoreError> { + debug!("Updating existing InboundGroupSession with new SenderData"); + use crate::olm::sender_data_finder::SessionDeviceCheckError::*; + + match SenderDataFinder::find_using_device_data(&self.store, device.clone(), session).await { + Ok(sender_data) => { + session.sender_data = sender_data; + } + Err(CryptoStoreError(e)) => { + return Err(e); + } + Err(MismatchedIdentityKeys(e)) => { + warn!( + ?session, + ?device, + "cannot update existing InboundGroupSession due to ownership error: {}", + e + ); + } + }; + + Ok(()) + } } /// Log information about what changed after processing a /keys/query response. @@ -2286,4 +2411,95 @@ pub(crate) mod tests { // The latch should be set now assert!(bob_identity.was_previously_verified()); } + + mod update_sender_data { + use assert_matches::assert_matches; + use matrix_sdk_test::async_test; + use ruma::room_id; + + use super::{device_id, manager_test_helper}; + use crate::{ + identities::manager::testing::{other_user_id, user_id}, + olm::{InboundGroupSession, SenderData}, + store::{Changes, DeviceChanges}, + Account, DeviceData, EncryptionSettings, + }; + + #[async_test] + async fn test_adds_device_info_to_existing_sessions() { + let manager = manager_test_helper(user_id(), device_id()).await; + + // Given that we have lots of sessions in the store, from each of two devices + let account1 = Account::new(user_id()); + let account2 = Account::new(other_user_id()); + + let mut account1_sessions = Vec::new(); + for _ in 0..60 { + account1_sessions.push(create_inbound_group_session(&account1).await); + } + let mut account2_sessions = Vec::new(); + for _ in 0..60 { + account2_sessions.push(create_inbound_group_session(&account2).await); + } + manager + .store + .save_changes(Changes { + inbound_group_sessions: [account1_sessions.clone(), account2_sessions.clone()] + .concat(), + ..Default::default() + }) + .await + .unwrap(); + + // When we get an update for one device + let device_data = DeviceData::from_account(&account1); + manager + .update_sender_data_from_device_changes(&DeviceChanges { + changed: vec![device_data], + ..Default::default() + }) + .await + .unwrap(); + + // Then those sessions should be updated + for session in account1_sessions { + let updated = manager + .store + .get_inbound_group_session(session.room_id(), session.session_id()) + .await + .unwrap() + .expect("Could not find session after update"); + assert_matches!( + updated.sender_data, + SenderData::DeviceInfo { .. }, + "incorrect sender data for session {}", + session.session_id() + ); + } + + // ... and those from the other account should not + for session in account2_sessions { + let updated = manager + .store + .get_inbound_group_session(session.room_id(), session.session_id()) + .await + .unwrap() + .expect("Could not find session after update"); + assert_matches!(updated.sender_data, SenderData::UnknownDevice { .. }); + } + } + + /// Create an InboundGroupSession sent from the given account + async fn create_inbound_group_session(account: &Account) -> InboundGroupSession { + let (_, igs) = account + .create_group_session_pair( + room_id!("!test:room"), + EncryptionSettings::default(), + SenderData::unknown(), + ) + .await + .unwrap(); + igs + } + } } diff --git a/crates/matrix-sdk-crypto/src/store/integration_tests.rs b/crates/matrix-sdk-crypto/src/store/integration_tests.rs index 24098de3f0e..5cbb61bfb17 100644 --- a/crates/matrix-sdk-crypto/src/store/integration_tests.rs +++ b/crates/matrix-sdk-crypto/src/store/integration_tests.rs @@ -648,12 +648,13 @@ macro_rules! cryptostore_integration_tests { // Check that there are exactly two results in the batch assert_eq!(sessions_1_k_batch.len(), 2); + previous_last_session_id = Some(last_session.session_id().to_owned()); + // Modify one of the results, to check that that doesn't break iteration let mut last_session = last_session.clone(); last_session.sender_data = SenderData::unknown(); store.save_inbound_group_sessions(vec![last_session], None).await.unwrap(); - previous_last_session_id = Some(last_session.session_id().to_owned()); sessions_1_k.append(&mut sessions_1_k_batch); } From 80948199ff9bf713684a6cd6e73779063c64fd12 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 30 Aug 2024 17:03:26 +0100 Subject: [PATCH 5/6] crypto: update senderdata integration tests Extend the integration tests for megolm sender data to check that we update existing inbound group sessions when we get a `/keys/query` response. --- .../src/identities/manager.rs | 5 +- .../src/machine/test_helpers.rs | 44 +++++++- .../src/machine/tests/megolm_sender_data.rs | 106 +++++++++++++++++- 3 files changed, 150 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/identities/manager.rs b/crates/matrix-sdk-crypto/src/identities/manager.rs index f3e7c40a9e8..28ab62eba87 100644 --- a/crates/matrix-sdk-crypto/src/identities/manager.rs +++ b/crates/matrix-sdk-crypto/src/identities/manager.rs @@ -1122,11 +1122,14 @@ impl IdentityManager { session: &mut InboundGroupSession, device: &DeviceData, ) -> Result<(), CryptoStoreError> { - debug!("Updating existing InboundGroupSession with new SenderData"); use crate::olm::sender_data_finder::SessionDeviceCheckError::*; match SenderDataFinder::find_using_device_data(&self.store, device.clone(), session).await { Ok(sender_data) => { + debug!( + "Updating existing InboundGroupSession with new SenderData {:?}", + sender_data + ); session.sender_data = sender_data; } Err(CryptoStoreError(e)) => { diff --git a/crates/matrix-sdk-crypto/src/machine/test_helpers.rs b/crates/matrix-sdk-crypto/src/machine/test_helpers.rs index 4d0c9059809..c1ace327dea 100644 --- a/crates/matrix-sdk-crypto/src/machine/test_helpers.rs +++ b/crates/matrix-sdk-crypto/src/machine/test_helpers.rs @@ -17,17 +17,24 @@ use std::collections::BTreeMap; +use as_variant::as_variant; use matrix_sdk_test::{ruma_response_from_json, test_json}; use ruma::{ - api::client::keys::{claim_keys, get_keys, upload_keys}, + api::client::keys::{ + claim_keys, get_keys, get_keys::v3::Response as KeysQueryResponse, upload_keys, + }, device_id, encryption::OneTimeKey, events::dummy::ToDeviceDummyEventContent, serde::Raw, user_id, DeviceId, OwnedDeviceKeyId, TransactionId, UserId, }; +use serde_json::json; -use crate::{store::Changes, types::events::ToDeviceEvent, DeviceData, OlmMachine}; +use crate::{ + store::Changes, types::events::ToDeviceEvent, CrossSigningBootstrapRequests, DeviceData, + OlmMachine, OutgoingRequests, +}; /// These keys need to be periodically uploaded to the server. type OneTimeKeys = BTreeMap>; @@ -182,3 +189,36 @@ pub async fn create_session( let response = claim_keys::v3::Response::new(one_time_keys); machine.inner.session_manager.create_sessions(&response).await.unwrap(); } + +/// Given a set of requests returned by `bootstrap_cross_signing` for one user, +/// return a `/keys/query` response which might be returned to another user/ +pub fn bootstrap_requests_to_keys_query_response( + bootstrap_requests: CrossSigningBootstrapRequests, +) -> KeysQueryResponse { + let mut kq_response = json!({}); + + // If we have a master key, add that to the response + if let Some(key) = bootstrap_requests.upload_signing_keys_req.master_key { + let user_id = key.user_id.clone(); + kq_response["master_keys"] = json!({user_id: key}); + } + + // If we have a self-signing key, add that + if let Some(key) = bootstrap_requests.upload_signing_keys_req.self_signing_key { + let user_id = key.user_id.clone(); + kq_response["self_signing_keys"] = json!({user_id: key}); + } + + // And if we have a device, add that + if let Some(dk) = bootstrap_requests + .upload_keys_req + .and_then(|req| as_variant!(req.request.as_ref(), OutgoingRequests::KeysUpload).cloned()) + .and_then(|keys_upload_request| keys_upload_request.device_keys) + { + let user_id: String = dk.get_field("user_id").unwrap().unwrap(); + let device_id: String = dk.get_field("device_id").unwrap().unwrap(); + kq_response["device_keys"] = json!({user_id: { device_id: dk }}); + } + + ruma_response_from_json(&kq_response) +} diff --git a/crates/matrix-sdk-crypto/src/machine/tests/megolm_sender_data.rs b/crates/matrix-sdk-crypto/src/machine/tests/megolm_sender_data.rs index 2ac6602e4e2..d4a12a998ae 100644 --- a/crates/matrix-sdk-crypto/src/machine/tests/megolm_sender_data.rs +++ b/crates/matrix-sdk-crypto/src/machine/tests/megolm_sender_data.rs @@ -26,13 +26,16 @@ use serde_json::json; use crate::{ machine::{ - test_helpers::get_machine_pair_with_setup_sessions_test_helper, + test_helpers::{ + bootstrap_requests_to_keys_query_response, + get_machine_pair_with_setup_sessions_test_helper, + }, tests::to_device_requests_to_content, }, olm::{InboundGroupSession, SenderData}, store::RoomKeyInfo, types::events::{room::encrypted::ToDeviceEncryptedEventContent, EventType, ToDeviceEvent}, - EncryptionSettings, EncryptionSyncChanges, OlmMachine, Session, + DeviceData, EncryptionSettings, EncryptionSyncChanges, OlmMachine, Session, }; /// Test the behaviour when a megolm session is received from an unknown device, @@ -105,6 +108,105 @@ async fn test_receive_megolm_session_from_known_device() { ); } +/// If we have a megolm session from an unknown device, test what happens when +/// we get a /keys/query response that includes that device. +#[async_test] +async fn test_update_unknown_device_senderdata_on_keys_query() { + // Given we have a megolm session from an unknown device + + let (alice, bob) = get_machine_pair().await; + let mut bob_room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream()); + + // `get_machine_pair_with_setup_sessions_test_helper` tells Bob about Alice's + // device keys, so to run this test, we need to make him forget them. + forget_devices_for_user(&bob, alice.user_id()).await; + + // Alice starts a megolm session and shares the key with Bob, *without* sending + // the sender data. + let room_id = room_id!("!test:example.org"); + let event = create_and_share_session_without_sender_data(&alice, &bob, room_id).await; + + // Bob receives the to-device message + receive_to_device_event(&bob, &event).await; + + // and now Bob should know about the session. + let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream); + let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await; + + // Double-check that it is, in fact, an unknown device session. + assert_matches!(session.sender_data, SenderData::UnknownDevice { .. }); + + // When Bob gets a /keys/query response for Alice, that includes the + // sending device... + + let alice_device = DeviceData::from_machine_test_helper(&alice).await.unwrap(); + let kq_response = json!({ + "device_keys": { alice.user_id() : { alice.device_id(): alice_device.as_device_keys()}} + }); + bob.receive_keys_query_response( + &TransactionId::new(), + &matrix_sdk_test::ruma_response_from_json(&kq_response), + ) + .await + .unwrap(); + + // Then Bob should have received an update about the session, and it should now + // be `SenderData::DeviceInfo` + let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream); + let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await; + + assert_matches!( + session.sender_data, + SenderData::DeviceInfo {legacy_session, ..} => { + assert!(legacy_session); // TODO: change when https://github.com/matrix-org/matrix-rust-sdk/pull/3785 lands + } + ); +} + +/// If we have a megolm session from an unsigned device, test what happens when +/// we get a /keys/query response that includes that device. +#[async_test] +async fn test_update_device_info_senderdata_on_keys_query() { + // Given we have a megolm session from an unsigned device + + let (alice, bob) = get_machine_pair().await; + let mut bob_room_keys_received_stream = Box::pin(bob.store().room_keys_received_stream()); + + // Alice starts a megolm session and shares the key with Bob + let room_id = room_id!("!test:example.org"); + + let to_device_requests = alice + .share_room_key(room_id, iter::once(bob.user_id()), EncryptionSettings::default()) + .await + .unwrap(); + let event = ToDeviceEvent::new( + alice.user_id().to_owned(), + to_device_requests_to_content(to_device_requests), + ); + // Bob receives the to-device message + receive_to_device_event(&bob, &event).await; + + // and now Bob should know about the session. + let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream); + let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await; + + // Double-check that it is, in fact, an unverified device session. + assert_matches!(session.sender_data, SenderData::DeviceInfo { .. }); + + // When Bob receives a /keys/query response for Alice that includes a verifiable + // signature for her device + let bootstrap_requests = alice.bootstrap_cross_signing(false).await.unwrap(); + let kq_response = bootstrap_requests_to_keys_query_response(bootstrap_requests); + bob.receive_keys_query_response(&TransactionId::new(), &kq_response).await.unwrap(); + + // Then Bob should have received an update about the session, and it should now + // be `SenderData::SenderUnverified` + let room_key_info = get_room_key_received_update(&mut bob_room_keys_received_stream); + let session = get_inbound_group_session_or_panic(&bob, &room_key_info).await; + + assert_matches!(session.sender_data, SenderData::SenderUnverified(_)); +} + /// Convenience wrapper for [`get_machine_pair_with_setup_sessions_test_helper`] /// using standard user ids. async fn get_machine_pair() -> (OlmMachine, OlmMachine) { From dbc852b411a2593ad8e7be1db213134baac3d84b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 30 Aug 2024 17:06:29 +0100 Subject: [PATCH 6/6] crypto: update changelog --- crates/matrix-sdk-crypto/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/matrix-sdk-crypto/CHANGELOG.md b/crates/matrix-sdk-crypto/CHANGELOG.md index a463ee92a2e..a702c317fcf 100644 --- a/crates/matrix-sdk-crypto/CHANGELOG.md +++ b/crates/matrix-sdk-crypto/CHANGELOG.md @@ -2,6 +2,10 @@ Changes: +- Update `SenderData` on existing inbound group sessions when we receive + updates via `/keys/query`. + ([#3849](https://github.com/matrix-org/matrix-rust-sdk/pull/3849)) + - Add message IDs to all outgoing to-device messages encrypted by `matrix-sdk-crypto`. The `message-ids` feature of `matrix-sdk-crypto` and `matrix-sdk-base` is now a no-op.