From 4be4d3985136eda0a51d1e70f0a6804404aa5d75 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 11 Apr 2025 16:58:20 +0100 Subject: [PATCH 1/6] crypto: add types to support decryption of RoomKeyHistoryBundle to-device events --- crates/matrix-sdk-crypto/src/machine/mod.rs | 7 ++++-- .../src/machine/tests/mod.rs | 3 ++- .../src/types/events/olm_v1.rs | 22 +++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/machine/mod.rs b/crates/matrix-sdk-crypto/src/machine/mod.rs index 908e52492..ee6f0cda0 100644 --- a/crates/matrix-sdk-crypto/src/machine/mod.rs +++ b/crates/matrix-sdk-crypto/src/machine/mod.rs @@ -75,11 +75,11 @@ use crate::{ store::{ Changes, CryptoStoreWrapper, DeviceChanges, IdentityChanges, IntoCryptoStore, MemoryStore, PendingChanges, Result as StoreResult, RoomKeyInfo, RoomSettings, SecretImportError, Store, - StoreCache, StoreTransaction, + StoreCache, StoreTransaction, StoredRoomKeyBundleData, }, types::{ events::{ - olm_v1::{AnyDecryptedOlmEvent, DecryptedRoomKeyEvent}, + olm_v1::{AnyDecryptedOlmEvent, DecryptedRoomKeyBundleEvent, DecryptedRoomKeyEvent}, room::encrypted::{ EncryptedEvent, EncryptedToDeviceEvent, RoomEncryptedEventContent, RoomEventEncryptionScheme, SupportedEventEncryptionSchemes, @@ -1184,6 +1184,9 @@ impl OlmMachine { AnyDecryptedOlmEvent::Dummy(_) => { debug!("Received an `m.dummy` event"); } + AnyDecryptedOlmEvent::RoomKeyBundle(e) => { + debug!("Received a room key bundle event {:?}", e); + } AnyDecryptedOlmEvent::Custom(_) => { warn!("Received an unexpected encrypted to-device event"); } diff --git a/crates/matrix-sdk-crypto/src/machine/tests/mod.rs b/crates/matrix-sdk-crypto/src/machine/tests/mod.rs index c464d1f05..01825e112 100644 --- a/crates/matrix-sdk-crypto/src/machine/tests/mod.rs +++ b/crates/matrix-sdk-crypto/src/machine/tests/mod.rs @@ -33,7 +33,7 @@ use ruma::{ AddMentions, MessageType, Relation, ReplyWithinThread, RoomMessageEventContent, }, AnyMessageLikeEvent, AnyMessageLikeEventContent, AnyToDeviceEvent, MessageLikeEvent, - OriginalMessageLikeEvent, + OriginalMessageLikeEvent, ToDeviceEventType, }, room_id, serde::Raw, @@ -115,6 +115,7 @@ pub fn to_device_requests_to_content( requests: Vec>, ) -> ToDeviceEncryptedEventContent { let to_device_request = &requests[0]; + assert_eq!(to_device_request.event_type, ToDeviceEventType::RoomEncrypted); to_device_request .messages diff --git a/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs b/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs index 9dfd68e11..0c116a961 100644 --- a/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs +++ b/crates/matrix-sdk-crypto/src/types/events/olm_v1.rs @@ -30,7 +30,11 @@ use super::{ secret_send::SecretSendContent, EventType, }; -use crate::types::{deserialize_ed25519_key, events::from_str, serialize_ed25519_key, DeviceKeys}; +use crate::types::{ + deserialize_ed25519_key, + events::{from_str, room_key_bundle::RoomKeyBundleContent}, + serialize_ed25519_key, DeviceKeys, +}; /// An `m.dummy` event that was decrypted using the /// `m.olm.v1.curve25519-aes-sha2` algorithm @@ -77,6 +81,10 @@ impl DecryptedForwardedRoomKeyEvent { /// `m.olm.v1.curve25519-aes-sha2` algorithm pub type DecryptedSecretSendEvent = DecryptedOlmV1Event; +/// An `io.element.msc4268.room_key_bundle` to-device event which has +/// been decrypted using using the `m.olm.v1.curve25519-aes-sha2` algorithm +pub type DecryptedRoomKeyBundleEvent = DecryptedOlmV1Event; + /// An enum over the various events that were decrypted using the /// `m.olm.v1.curve25519-aes-sha2` algorithm. #[derive(Debug)] @@ -89,6 +97,8 @@ pub enum AnyDecryptedOlmEvent { SecretSend(DecryptedSecretSendEvent), /// The `m.dummy` decrypted to-device event. Dummy(DecryptedDummyEvent), + /// The `io.element.msc4268.room_key_bundle` decrypted to-device event. + RoomKeyBundle(DecryptedRoomKeyBundleEvent), /// A decrypted to-device event of an unknown or custom type. Custom(Box), } @@ -101,6 +111,7 @@ impl AnyDecryptedOlmEvent { AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.sender, AnyDecryptedOlmEvent::SecretSend(e) => &e.sender, AnyDecryptedOlmEvent::Custom(e) => &e.sender, + AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.sender, AnyDecryptedOlmEvent::Dummy(e) => &e.sender, } } @@ -112,6 +123,7 @@ impl AnyDecryptedOlmEvent { AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.recipient, AnyDecryptedOlmEvent::SecretSend(e) => &e.recipient, AnyDecryptedOlmEvent::Custom(e) => &e.recipient, + AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.recipient, AnyDecryptedOlmEvent::Dummy(e) => &e.recipient, } } @@ -123,6 +135,7 @@ impl AnyDecryptedOlmEvent { AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.keys, AnyDecryptedOlmEvent::SecretSend(e) => &e.keys, AnyDecryptedOlmEvent::Custom(e) => &e.keys, + AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.keys, AnyDecryptedOlmEvent::Dummy(e) => &e.keys, } } @@ -134,6 +147,7 @@ impl AnyDecryptedOlmEvent { AnyDecryptedOlmEvent::ForwardedRoomKey(e) => &e.recipient_keys, AnyDecryptedOlmEvent::SecretSend(e) => &e.recipient_keys, AnyDecryptedOlmEvent::Custom(e) => &e.recipient_keys, + AnyDecryptedOlmEvent::RoomKeyBundle(e) => &e.recipient_keys, AnyDecryptedOlmEvent::Dummy(e) => &e.recipient_keys, } } @@ -145,6 +159,7 @@ impl AnyDecryptedOlmEvent { AnyDecryptedOlmEvent::RoomKey(e) => e.content.event_type(), AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.content.event_type(), AnyDecryptedOlmEvent::SecretSend(e) => e.content.event_type(), + AnyDecryptedOlmEvent::RoomKeyBundle(e) => e.content.event_type(), AnyDecryptedOlmEvent::Dummy(e) => e.content.event_type(), } } @@ -156,6 +171,7 @@ impl AnyDecryptedOlmEvent { AnyDecryptedOlmEvent::RoomKey(e) => e.sender_device_keys.as_ref(), AnyDecryptedOlmEvent::ForwardedRoomKey(e) => e.sender_device_keys.as_ref(), AnyDecryptedOlmEvent::SecretSend(e) => e.sender_device_keys.as_ref(), + AnyDecryptedOlmEvent::RoomKeyBundle(e) => e.sender_device_keys.as_ref(), AnyDecryptedOlmEvent::Dummy(e) => e.sender_device_keys.as_ref(), } } @@ -315,7 +331,9 @@ impl<'de> Deserialize<'de> for AnyDecryptedOlmEvent { "m.forwarded_room_key" => AnyDecryptedOlmEvent::ForwardedRoomKey(from_str(json)?), "m.secret.send" => AnyDecryptedOlmEvent::SecretSend(from_str(json)?), "m.dummy" => AnyDecryptedOlmEvent::Dummy(from_str(json)?), - + RoomKeyBundleContent::EVENT_TYPE => { + AnyDecryptedOlmEvent::RoomKeyBundle(from_str(json)?) + } _ => AnyDecryptedOlmEvent::Custom(from_str(json)?), }) } From 3aa0983a5c89da50c5aeab1d0ce99e36f2ec21bc Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 14 Apr 2025 11:38:04 +0100 Subject: [PATCH 2/6] crypto: add received room key bundles to store changes list After we receive a to-device message holding room key bundle info, add the data to the store's Changes structure --- crates/matrix-sdk-crypto/src/machine/mod.rs | 21 +++++++++++++++++ crates/matrix-sdk-crypto/src/store/mod.rs | 26 +++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/machine/mod.rs b/crates/matrix-sdk-crypto/src/machine/mod.rs index ee6f0cda0..f44540510 100644 --- a/crates/matrix-sdk-crypto/src/machine/mod.rs +++ b/crates/matrix-sdk-crypto/src/machine/mod.rs @@ -939,6 +939,26 @@ impl OlmMachine { } } + #[instrument()] + async fn receive_room_key_bundle( + &self, + sender_key: Curve25519PublicKey, + event: &DecryptedRoomKeyBundleEvent, + changes: &mut Changes, + ) -> OlmResult<()> { + let Some(sender_device_keys) = &event.sender_device_keys else { + warn!("Received a room key bundle with no sender device keys: ignoring"); + return Ok(()); + }; + + changes.received_room_key_bundles.push(StoredRoomKeyBundleData { + sender_user: event.sender.clone(), + sender_data: SenderData::device_info(sender_device_keys.clone()), + bundle_data: event.content.clone(), + }); + Ok(()) + } + fn add_withheld_info(&self, changes: &mut Changes, event: &RoomKeyWithheldEvent) { debug!(?event.content, "Processing `m.room_key.withheld` event"); @@ -1186,6 +1206,7 @@ impl OlmMachine { } AnyDecryptedOlmEvent::RoomKeyBundle(e) => { debug!("Received a room key bundle event {:?}", e); + self.receive_room_key_bundle(decrypted.result.sender_key, e, changes).await?; } AnyDecryptedOlmEvent::Custom(_) => { warn!("Received an unexpected encrypted to-device event"); diff --git a/crates/matrix-sdk-crypto/src/store/mod.rs b/crates/matrix-sdk-crypto/src/store/mod.rs index f5dc8c58b..6e2fd251b 100644 --- a/crates/matrix-sdk-crypto/src/store/mod.rs +++ b/crates/matrix-sdk-crypto/src/store/mod.rs @@ -70,7 +70,7 @@ use crate::{ identities::{user::UserIdentity, Device, DeviceData, UserDevices, UserIdentityData}, olm::{ Account, ExportedRoomKey, InboundGroupSession, OlmMessageHash, OutboundGroupSession, - PrivateCrossSigningIdentity, Session, StaticAccountData, + PrivateCrossSigningIdentity, SenderData, Session, StaticAccountData, }, types::{ events::room_key_withheld::RoomKeyWithheldEvent, BackupSecrets, CrossSigningSecrets, @@ -101,7 +101,8 @@ pub use memorystore::MemoryStore; pub use traits::{CryptoStore, DynCryptoStore, IntoCryptoStore}; use crate::types::{ - events::room_key_withheld::RoomKeyWithheldContent, room_history::RoomKeyBundle, + events::{room_key_bundle::RoomKeyBundleContent, room_key_withheld::RoomKeyWithheldContent}, + room_history::RoomKeyBundle, }; pub use crate::{ dehydrated_devices::DehydrationError, @@ -541,6 +542,26 @@ pub struct Changes { pub room_settings: HashMap, pub secrets: Vec, pub next_batch_token: Option, + + /// Historical room key history bundles that we have received and should + /// store. + pub received_room_key_bundles: Vec, +} + +/// Information about an [MSC4268] room key bundle. +/// +/// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268 +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct StoredRoomKeyBundleData { + /// The user that sent us this data. + pub sender_user: OwnedUserId, + + /// Information about the sender of this data and how much we trust that + /// information. + pub sender_data: SenderData, + + /// The room key bundle data itself. + pub bundle_data: RoomKeyBundleContent, } /// A user for which we are tracking the list of devices. @@ -573,6 +594,7 @@ impl Changes { && self.room_settings.is_empty() && self.secrets.is_empty() && self.next_batch_token.is_none() + && self.received_room_key_bundles.is_empty() } } From 00364d95af88b669b42f5131d455ec96ad030ef5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 14 Apr 2025 18:15:16 +0100 Subject: [PATCH 3/6] crypto: add methods for room key bundles to store traits --- .../src/store/integration_tests.rs | 54 ++++++++++++++++++- .../src/store/memorystore.rs | 19 ++++++- crates/matrix-sdk-crypto/src/store/traits.rs | 18 ++++++- .../src/crypto_store/mod.rs | 6 ++- crates/matrix-sdk-sqlite/src/crypto_store.rs | 10 +++- 5 files changed, 102 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/store/integration_tests.rs b/crates/matrix-sdk-crypto/src/store/integration_tests.rs index 2c0f9baf5..91a66c6ac 100644 --- a/crates/matrix-sdk-crypto/src/store/integration_tests.rs +++ b/crates/matrix-sdk-crypto/src/store/integration_tests.rs @@ -52,7 +52,7 @@ macro_rules! cryptostore_integration_tests { }, store::{ BackupDecryptionKey, Changes, CryptoStore, DehydratedDeviceKey, DeviceChanges, GossipRequest, - IdentityChanges, PendingChanges, RoomSettings, + IdentityChanges, PendingChanges, RoomSettings, StoredRoomKeyBundleData, }, testing::{get_device, get_other_identity, get_own_identity}, types::{ @@ -64,6 +64,7 @@ macro_rules! cryptostore_integration_tests { CommonWithheldCodeContent, MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent, }, + room_key_bundle::RoomKeyBundleContent, secret_send::SecretSendContent, ToDeviceEvent, }, @@ -1276,6 +1277,57 @@ macro_rules! cryptostore_integration_tests { assert_eq!(None, loaded_2); } + #[async_test] + #[ignore] // not yet implemented for all stores + async fn test_received_room_key_bundle() { + let store = get_store("received_room_key_bundle", None, true).await; + let test_room = room_id!("!room:example.org"); + + fn make_bundle_data(sender_user: &UserId, bundle_uri: &str) -> StoredRoomKeyBundleData { + let jwk = ruma::events::room::JsonWebKeyInit { + kty: "oct".to_owned(), + key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()], + alg: "A256CTR".to_owned(), + k: ruma::serde::Base64::new(vec![0u8; 0]), + ext: true, + }.into(); + + let file = ruma::events::room::EncryptedFileInit { + url: ruma::OwnedMxcUri::from(bundle_uri), + key: jwk, + iv: ruma::serde::Base64::new(vec![0u8; 0]), + hashes: Default::default(), + v: "".to_owned(), + }.into(); + + StoredRoomKeyBundleData { + sender_user: sender_user.to_owned(), + sender_data: SenderData::unknown(), + bundle_data: RoomKeyBundleContent { + room_id: room_id!("!room:example.org").to_owned(), + file, + }, + } + } + + // Add three entries + let changes = Changes { + received_room_key_bundles: vec![ + make_bundle_data(user_id!("@alice:example.com"), "alice1"), + make_bundle_data(user_id!("@bob:example.com"), "bob1"), + make_bundle_data(user_id!("@alice:example.com"), "alice2"), + ], + ..Default::default() + }; + store.save_changes(changes).await.unwrap(); + + // Check we get the right one + let bundle = store.get_received_room_key_bundle_data( + test_room, user_id!("@alice:example.com") + ).await.unwrap().expect("Did not get any bundle data"); + assert_eq!(bundle.bundle_data.file.url.to_string(), "alice2"); + } + fn session_info(session: &InboundGroupSession) -> (&RoomId, &str) { (&session.room_id(), &session.session_id()) } diff --git a/crates/matrix-sdk-crypto/src/store/memorystore.rs b/crates/matrix-sdk-crypto/src/store/memorystore.rs index 89ade3e64..55d927e20 100644 --- a/crates/matrix-sdk-crypto/src/store/memorystore.rs +++ b/crates/matrix-sdk-crypto/src/store/memorystore.rs @@ -33,6 +33,7 @@ use vodozemac::Curve25519PublicKey; use super::{ caches::DeviceStore, Account, BackupKeys, Changes, CryptoStore, DehydratedDeviceKey, InboundGroupSession, PendingChanges, RoomKeyCounts, RoomSettings, Session, + StoredRoomKeyBundleData, }; use crate::{ gossiping::{GossipRequest, GossippedSecret, SecretInfo}, @@ -719,6 +720,14 @@ impl CryptoStore for MemoryStore { Ok(self.room_settings.read().get(room_id).cloned()) } + async fn get_received_room_key_bundle_data( + &self, + _room_id: &RoomId, + _user_id: &UserId, + ) -> Result> { + todo!() + } + async fn get_custom_value(&self, key: &str) -> Result>> { Ok(self.custom_values.read().get(key).cloned()) } @@ -1249,7 +1258,7 @@ mod integration_tests { }, store::{ BackupKeys, Changes, CryptoStore, DehydratedDeviceKey, PendingChanges, RoomKeyCounts, - RoomSettings, + RoomSettings, StoredRoomKeyBundleData, }, types::events::room_key_withheld::RoomKeyWithheldEvent, Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, Session, TrackedUser, @@ -1511,6 +1520,14 @@ mod integration_tests { self.0.get_room_settings(room_id).await } + async fn get_received_room_key_bundle_data( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> crate::store::Result, Self::Error> { + self.0.get_received_room_key_bundle_data(room_id, user_id).await + } + async fn get_custom_value(&self, key: &str) -> Result>, Self::Error> { self.0.get_custom_value(key).await } diff --git a/crates/matrix-sdk-crypto/src/store/traits.rs b/crates/matrix-sdk-crypto/src/store/traits.rs index 3e651061d..b4064dc7b 100644 --- a/crates/matrix-sdk-crypto/src/store/traits.rs +++ b/crates/matrix-sdk-crypto/src/store/traits.rs @@ -23,7 +23,7 @@ use vodozemac::Curve25519PublicKey; use super::{ BackupKeys, Changes, CryptoStoreError, DehydratedDeviceKey, PendingChanges, Result, - RoomKeyCounts, RoomSettings, + RoomKeyCounts, RoomSettings, StoredRoomKeyBundleData, }; #[cfg(doc)] use crate::olm::SenderData; @@ -323,6 +323,14 @@ pub trait CryptoStore: AsyncTraitDeps { room_id: &RoomId, ) -> Result, Self::Error>; + /// Get the details about the room key bundle data received from the given + /// user for the given room. + async fn get_received_room_key_bundle_data( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result, Self::Error>; + /// Get arbitrary data from the store /// /// # Arguments @@ -569,6 +577,14 @@ impl CryptoStore for EraseCryptoStoreError { self.0.get_room_settings(room_id).await.map_err(Into::into) } + async fn get_received_room_key_bundle_data( + &self, + room_id: &RoomId, + user_id: &UserId, + ) -> Result> { + self.0.get_received_room_key_bundle_data(room_id, user_id).await.map_err(Into::into) + } + async fn get_custom_value(&self, key: &str) -> Result>, Self::Error> { self.0.get_custom_value(key).await.map_err(Into::into) } diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs index 7f0658956..3f0cae194 100644 --- a/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs +++ b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs @@ -30,7 +30,7 @@ use matrix_sdk_crypto::{ }, store::{ BackupKeys, Changes, CryptoStore, CryptoStoreError, DehydratedDeviceKey, PendingChanges, - RoomKeyCounts, RoomSettings, + RoomKeyCounts, RoomSettings, StoredRoomKeyBundleData, }, types::events::room_key_withheld::RoomKeyWithheldEvent, vodozemac::base64_encode, @@ -1359,6 +1359,10 @@ impl_crypto_store! { .transpose() } + async fn get_received_room_key_bundle_data(&self, _room_id: &RoomId, _user_id: &UserId) -> Result> { + todo!() + } + async fn get_custom_value(&self, key: &str) -> Result>> { self .inner diff --git a/crates/matrix-sdk-sqlite/src/crypto_store.rs b/crates/matrix-sdk-sqlite/src/crypto_store.rs index 4f4360533..23b243aa6 100644 --- a/crates/matrix-sdk-sqlite/src/crypto_store.rs +++ b/crates/matrix-sdk-sqlite/src/crypto_store.rs @@ -29,7 +29,7 @@ use matrix_sdk_crypto::{ }, store::{ BackupKeys, Changes, CryptoStore, DehydratedDeviceKey, PendingChanges, RoomKeyCounts, - RoomSettings, + RoomSettings, StoredRoomKeyBundleData, }, types::events::room_key_withheld::RoomKeyWithheldEvent, Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, TrackedUser, UserIdentityData, @@ -1337,6 +1337,14 @@ impl CryptoStore for SqliteCryptoStore { return Ok(Some(settings)); } + async fn get_received_room_key_bundle_data( + &self, + _room_id: &RoomId, + _user_id: &UserId, + ) -> Result> { + todo!() + } + async fn get_custom_value(&self, key: &str) -> Result>> { let Some(serialized) = self.acquire().await?.get_kv(key).await? else { return Ok(None); From 6173aef064ec100033a8f6e9aca01f46085999f9 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 14 Apr 2025 11:40:20 +0100 Subject: [PATCH 4/6] memorystore: store received room key bundle data --- .../src/store/memorystore.rs | 54 ++++++++----------- 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/store/memorystore.rs b/crates/matrix-sdk-crypto/src/store/memorystore.rs index 55d927e20..a77fc2215 100644 --- a/crates/matrix-sdk-crypto/src/store/memorystore.rs +++ b/crates/matrix-sdk-crypto/src/store/memorystore.rs @@ -72,7 +72,7 @@ impl BackupVersion { } /// An in-memory only store that will forget all the E2EE key once it's dropped. -#[derive(Debug)] +#[derive(Default, Debug)] pub struct MemoryStore { static_account: Arc>>, @@ -103,36 +103,10 @@ pub struct MemoryStore { dehydrated_device_pickle_key: RwLock>, next_batch_token: RwLock>, room_settings: StdRwLock>, - save_changes_lock: Arc>, -} + room_key_bundles: + StdRwLock>>, -impl Default for MemoryStore { - fn default() -> Self { - MemoryStore { - static_account: Default::default(), - account: Default::default(), - sessions: Default::default(), - inbound_group_sessions: Default::default(), - inbound_group_sessions_backed_up_to: Default::default(), - outbound_group_sessions: Default::default(), - private_identity: Default::default(), - tracked_users: Default::default(), - olm_hashes: Default::default(), - devices: DeviceStore::new(), - identities: Default::default(), - outgoing_key_requests: Default::default(), - key_requests_by_info: Default::default(), - direct_withheld_info: Default::default(), - custom_values: Default::default(), - leases: Default::default(), - backup_keys: Default::default(), - dehydrated_device_pickle_key: Default::default(), - secret_inbox: Default::default(), - next_batch_token: Default::default(), - room_settings: Default::default(), - save_changes_lock: Default::default(), - } - } + save_changes_lock: Arc>, } impl MemoryStore { @@ -349,6 +323,16 @@ impl CryptoStore for MemoryStore { settings.extend(changes.room_settings); } + if !changes.received_room_key_bundles.is_empty() { + let mut room_key_bundles = self.room_key_bundles.write(); + for bundle in changes.received_room_key_bundles { + room_key_bundles + .entry(bundle.bundle_data.room_id.clone()) + .or_default() + .insert(bundle.sender_user.clone(), bundle); + } + } + Ok(()) } @@ -722,10 +706,14 @@ impl CryptoStore for MemoryStore { async fn get_received_room_key_bundle_data( &self, - _room_id: &RoomId, - _user_id: &UserId, + room_id: &RoomId, + user_id: &UserId, ) -> Result> { - todo!() + let guard = self.room_key_bundles.read(); + + let result = guard.get(room_id).and_then(|bundles| bundles.get(user_id).cloned()); + + Ok(result) } async fn get_custom_value(&self, key: &str) -> Result>> { From e89c45ba4245245cc2bd7c36a3616e845e5f7a53 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 14 Apr 2025 12:31:28 +0100 Subject: [PATCH 5/6] sqlite: store data on received room key bundles --- .../010_received_room_key_bundles.sql | 9 +++ crates/matrix-sdk-sqlite/src/crypto_store.rs | 71 +++++++++++++++++-- 2 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 crates/matrix-sdk-sqlite/migrations/crypto_store/010_received_room_key_bundles.sql diff --git a/crates/matrix-sdk-sqlite/migrations/crypto_store/010_received_room_key_bundles.sql b/crates/matrix-sdk-sqlite/migrations/crypto_store/010_received_room_key_bundles.sql new file mode 100644 index 000000000..f9748a488 --- /dev/null +++ b/crates/matrix-sdk-sqlite/migrations/crypto_store/010_received_room_key_bundles.sql @@ -0,0 +1,9 @@ +CREATE TABLE "received_room_key_bundle" +( + "room_id" BLOB NOT NULL, + "sender_user_id" BLOB NOT NULL, + "bundle_data" BLOB NOT NULL +); + +CREATE UNIQUE INDEX "received_room_key_bundle_room_id_user_id_idx" + ON "received_room_key_bundle" ("room_id", "sender_user_id"); diff --git a/crates/matrix-sdk-sqlite/src/crypto_store.rs b/crates/matrix-sdk-sqlite/src/crypto_store.rs index 23b243aa6..4dd831a0a 100644 --- a/crates/matrix-sdk-sqlite/src/crypto_store.rs +++ b/crates/matrix-sdk-sqlite/src/crypto_store.rs @@ -111,6 +111,7 @@ impl SqliteCryptoStore { let conn = pool.get().await?; let version = conn.db_version().await?; + debug!("Opened sqlite store with version {}", version); run_migrations(&conn, version).await?; let store_cipher = match passphrase { @@ -207,7 +208,7 @@ impl SqliteCryptoStore { } } -const DATABASE_VERSION: u8 = 9; +const DATABASE_VERSION: u8 = 10; /// key for the dehydrated device pickle key in the key/value table. const DEHYDRATED_DEVICE_PICKLE_KEY: &str = "dehydrated_device_pickle_key"; @@ -303,6 +304,16 @@ async fn run_migrations(conn: &SqliteAsyncConn, version: u8) -> Result<()> { .await?; } + if version < 10 { + conn.with_transaction(|txn| { + txn.execute_batch(include_str!( + "../migrations/crypto_store/010_received_room_key_bundles.sql" + ))?; + txn.set_db_version(10) + }) + .await?; + } + Ok(()) } @@ -350,6 +361,13 @@ trait SqliteConnectionExt { fn set_room_settings(&self, room_id: &[u8], data: &[u8]) -> rusqlite::Result<()>; fn set_secret(&self, request_id: &[u8], data: &[u8]) -> rusqlite::Result<()>; + + fn set_received_room_key_bundle( + &self, + room_id: &[u8], + user_id: &[u8], + data: &[u8], + ) -> rusqlite::Result<()>; } impl SqliteConnectionExt for rusqlite::Connection { @@ -478,6 +496,21 @@ impl SqliteConnectionExt for rusqlite::Connection { Ok(()) } + + fn set_received_room_key_bundle( + &self, + room_id: &[u8], + sender_user_id: &[u8], + data: &[u8], + ) -> rusqlite::Result<()> { + self.execute( + "INSERT INTO received_room_key_bundle(room_id, sender_user_id, bundle_data) + VALUES (?1, ?2, ?3) + ON CONFLICT (room_id, sender_user_id) DO UPDATE SET bundle_data = ?3", + (room_id, sender_user_id, data), + )?; + Ok(()) + } } #[async_trait] @@ -744,6 +777,21 @@ trait SqliteObjectCryptoStoreExt: SqliteAsyncConnExt { .await .optional()?) } + + async fn get_received_room_key_bundle( + &self, + room_id: Key, + sender_user: Key, + ) -> Result>> { + Ok(self + .query_row( + "SELECT bundle_data FROM received_room_key_bundle WHERE room_id = ? AND sender_user = ?", + (room_id, sender_user), + |row| { row.get(0) }, + ) + .await + .optional()?) + } } #[async_trait] @@ -947,6 +995,14 @@ impl CryptoStore for SqliteCryptoStore { txn.set_secret(&secret_name, &value)?; } + for bundle in changes.received_room_key_bundles { + let room_id = + this.encode_key("received_room_key_bundle", &bundle.bundle_data.room_id); + let user_id = this.encode_key("received_room_key_bundle", &bundle.sender_user); + let value = this.serialize_value(&bundle)?; + txn.set_received_room_key_bundle(&room_id, &user_id, &value)?; + } + Ok::<_, Error>(()) }) .await?; @@ -1339,10 +1395,17 @@ impl CryptoStore for SqliteCryptoStore { async fn get_received_room_key_bundle_data( &self, - _room_id: &RoomId, - _user_id: &UserId, + room_id: &RoomId, + user_id: &UserId, ) -> Result> { - todo!() + let room_id = self.encode_key("received_room_key_bundle", room_id); + let user_id = self.encode_key("received_room_key_bundle", user_id); + self.acquire() + .await? + .get_received_room_key_bundle(room_id, user_id) + .await? + .map(|value| self.deserialize_value(&value)) + .transpose() } async fn get_custom_value(&self, key: &str) -> Result>> { From 85e0626d5f85aa2a8b7730de91ab442b4de351ea Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 16 Apr 2025 16:28:32 +0100 Subject: [PATCH 6/6] indexeddb: fudge implementation of `get_received_room_key_bundle_data` --- crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs index 3f0cae194..520c3d038 100644 --- a/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs +++ b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs @@ -1359,8 +1359,10 @@ impl_crypto_store! { .transpose() } + #[allow(clippy::unused_async)] async fn get_received_room_key_bundle_data(&self, _room_id: &RoomId, _user_id: &UserId) -> Result> { - todo!() + // TODO: not yet implemented for indexeddb + Ok(None) } async fn get_custom_value(&self, key: &str) -> Result>> {