Skip to content

Commit 468e7c3

Browse files
authored
Merge pull request #4932 from matrix-org/rav/history_sharing/save_key_bundle_data
crypto: store received room key bundle data information Add hooks to the memory store and sqlite store to stash the information about room key data.
2 parents 1554e05 + a3cb1cd commit 468e7c3

File tree

10 files changed

+267
-43
lines changed

10 files changed

+267
-43
lines changed

crates/matrix-sdk-crypto/src/machine/mod.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ use crate::{
7575
store::{
7676
Changes, CryptoStoreWrapper, DeviceChanges, IdentityChanges, IntoCryptoStore, MemoryStore,
7777
PendingChanges, Result as StoreResult, RoomKeyInfo, RoomSettings, SecretImportError, Store,
78-
StoreCache, StoreTransaction,
78+
StoreCache, StoreTransaction, StoredRoomKeyBundleData,
7979
},
8080
types::{
8181
events::{
82-
olm_v1::{AnyDecryptedOlmEvent, DecryptedRoomKeyEvent},
82+
olm_v1::{AnyDecryptedOlmEvent, DecryptedRoomKeyBundleEvent, DecryptedRoomKeyEvent},
8383
room::encrypted::{
8484
EncryptedEvent, EncryptedToDeviceEvent, RoomEncryptedEventContent,
8585
RoomEventEncryptionScheme, SupportedEventEncryptionSchemes,
@@ -939,6 +939,26 @@ impl OlmMachine {
939939
}
940940
}
941941

942+
#[instrument()]
943+
async fn receive_room_key_bundle(
944+
&self,
945+
sender_key: Curve25519PublicKey,
946+
event: &DecryptedRoomKeyBundleEvent,
947+
changes: &mut Changes,
948+
) -> OlmResult<()> {
949+
let Some(sender_device_keys) = &event.sender_device_keys else {
950+
warn!("Received a room key bundle with no sender device keys: ignoring");
951+
return Ok(());
952+
};
953+
954+
changes.received_room_key_bundles.push(StoredRoomKeyBundleData {
955+
sender_user: event.sender.clone(),
956+
sender_data: SenderData::device_info(sender_device_keys.clone()),
957+
bundle_data: event.content.clone(),
958+
});
959+
Ok(())
960+
}
961+
942962
fn add_withheld_info(&self, changes: &mut Changes, event: &RoomKeyWithheldEvent) {
943963
debug!(?event.content, "Processing `m.room_key.withheld` event");
944964

@@ -1184,6 +1204,10 @@ impl OlmMachine {
11841204
AnyDecryptedOlmEvent::Dummy(_) => {
11851205
debug!("Received an `m.dummy` event");
11861206
}
1207+
AnyDecryptedOlmEvent::RoomKeyBundle(e) => {
1208+
debug!("Received a room key bundle event {:?}", e);
1209+
self.receive_room_key_bundle(decrypted.result.sender_key, e, changes).await?;
1210+
}
11871211
AnyDecryptedOlmEvent::Custom(_) => {
11881212
warn!("Received an unexpected encrypted to-device event");
11891213
}

crates/matrix-sdk-crypto/src/machine/tests/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use ruma::{
3333
AddMentions, MessageType, Relation, ReplyWithinThread, RoomMessageEventContent,
3434
},
3535
AnyMessageLikeEvent, AnyMessageLikeEventContent, AnyToDeviceEvent, MessageLikeEvent,
36-
OriginalMessageLikeEvent,
36+
OriginalMessageLikeEvent, ToDeviceEventType,
3737
},
3838
room_id,
3939
serde::Raw,
@@ -115,6 +115,7 @@ pub fn to_device_requests_to_content(
115115
requests: Vec<Arc<ToDeviceRequest>>,
116116
) -> ToDeviceEncryptedEventContent {
117117
let to_device_request = &requests[0];
118+
assert_eq!(to_device_request.event_type, ToDeviceEventType::RoomEncrypted);
118119

119120
to_device_request
120121
.messages

crates/matrix-sdk-crypto/src/store/integration_tests.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ macro_rules! cryptostore_integration_tests {
5252
},
5353
store::{
5454
BackupDecryptionKey, Changes, CryptoStore, DehydratedDeviceKey, DeviceChanges, GossipRequest,
55-
IdentityChanges, PendingChanges, RoomSettings,
55+
IdentityChanges, PendingChanges, RoomSettings, StoredRoomKeyBundleData,
5656
},
5757
testing::{get_device, get_other_identity, get_own_identity},
5858
types::{
@@ -64,6 +64,7 @@ macro_rules! cryptostore_integration_tests {
6464
CommonWithheldCodeContent, MegolmV1AesSha2WithheldContent,
6565
RoomKeyWithheldContent,
6666
},
67+
room_key_bundle::RoomKeyBundleContent,
6768
secret_send::SecretSendContent,
6869
ToDeviceEvent,
6970
},
@@ -1276,6 +1277,57 @@ macro_rules! cryptostore_integration_tests {
12761277
assert_eq!(None, loaded_2);
12771278
}
12781279

1280+
#[async_test]
1281+
#[ignore] // not yet implemented for all stores
1282+
async fn test_received_room_key_bundle() {
1283+
let store = get_store("received_room_key_bundle", None, true).await;
1284+
let test_room = room_id!("!room:example.org");
1285+
1286+
fn make_bundle_data(sender_user: &UserId, bundle_uri: &str) -> StoredRoomKeyBundleData {
1287+
let jwk = ruma::events::room::JsonWebKeyInit {
1288+
kty: "oct".to_owned(),
1289+
key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()],
1290+
alg: "A256CTR".to_owned(),
1291+
k: ruma::serde::Base64::new(vec![0u8; 0]),
1292+
ext: true,
1293+
}.into();
1294+
1295+
let file = ruma::events::room::EncryptedFileInit {
1296+
url: ruma::OwnedMxcUri::from(bundle_uri),
1297+
key: jwk,
1298+
iv: ruma::serde::Base64::new(vec![0u8; 0]),
1299+
hashes: Default::default(),
1300+
v: "".to_owned(),
1301+
}.into();
1302+
1303+
StoredRoomKeyBundleData {
1304+
sender_user: sender_user.to_owned(),
1305+
sender_data: SenderData::unknown(),
1306+
bundle_data: RoomKeyBundleContent {
1307+
room_id: room_id!("!room:example.org").to_owned(),
1308+
file,
1309+
},
1310+
}
1311+
}
1312+
1313+
// Add three entries
1314+
let changes = Changes {
1315+
received_room_key_bundles: vec![
1316+
make_bundle_data(user_id!("@alice:example.com"), "alice1"),
1317+
make_bundle_data(user_id!("@bob:example.com"), "bob1"),
1318+
make_bundle_data(user_id!("@alice:example.com"), "alice2"),
1319+
],
1320+
..Default::default()
1321+
};
1322+
store.save_changes(changes).await.unwrap();
1323+
1324+
// Check we get the right one
1325+
let bundle = store.get_received_room_key_bundle_data(
1326+
test_room, user_id!("@alice:example.com")
1327+
).await.unwrap().expect("Did not get any bundle data");
1328+
assert_eq!(bundle.bundle_data.file.url.to_string(), "alice2");
1329+
}
1330+
12791331
fn session_info(session: &InboundGroupSession) -> (&RoomId, &str) {
12801332
(&session.room_id(), &session.session_id())
12811333
}

crates/matrix-sdk-crypto/src/store/memorystore.rs

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use vodozemac::Curve25519PublicKey;
3333
use super::{
3434
caches::DeviceStore, Account, BackupKeys, Changes, CryptoStore, DehydratedDeviceKey,
3535
InboundGroupSession, PendingChanges, RoomKeyCounts, RoomSettings, Session,
36+
StoredRoomKeyBundleData,
3637
};
3738
use crate::{
3839
gossiping::{GossipRequest, GossippedSecret, SecretInfo},
@@ -71,7 +72,7 @@ impl BackupVersion {
7172
}
7273

7374
/// An in-memory only store that will forget all the E2EE key once it's dropped.
74-
#[derive(Debug)]
75+
#[derive(Default, Debug)]
7576
pub struct MemoryStore {
7677
static_account: Arc<StdRwLock<Option<StaticAccountData>>>,
7778

@@ -102,36 +103,10 @@ pub struct MemoryStore {
102103
dehydrated_device_pickle_key: RwLock<Option<DehydratedDeviceKey>>,
103104
next_batch_token: RwLock<Option<String>>,
104105
room_settings: StdRwLock<HashMap<OwnedRoomId, RoomSettings>>,
105-
save_changes_lock: Arc<Mutex<()>>,
106-
}
106+
room_key_bundles:
107+
StdRwLock<HashMap<OwnedRoomId, HashMap<OwnedUserId, StoredRoomKeyBundleData>>>,
107108

108-
impl Default for MemoryStore {
109-
fn default() -> Self {
110-
MemoryStore {
111-
static_account: Default::default(),
112-
account: Default::default(),
113-
sessions: Default::default(),
114-
inbound_group_sessions: Default::default(),
115-
inbound_group_sessions_backed_up_to: Default::default(),
116-
outbound_group_sessions: Default::default(),
117-
private_identity: Default::default(),
118-
tracked_users: Default::default(),
119-
olm_hashes: Default::default(),
120-
devices: DeviceStore::new(),
121-
identities: Default::default(),
122-
outgoing_key_requests: Default::default(),
123-
key_requests_by_info: Default::default(),
124-
direct_withheld_info: Default::default(),
125-
custom_values: Default::default(),
126-
leases: Default::default(),
127-
backup_keys: Default::default(),
128-
dehydrated_device_pickle_key: Default::default(),
129-
secret_inbox: Default::default(),
130-
next_batch_token: Default::default(),
131-
room_settings: Default::default(),
132-
save_changes_lock: Default::default(),
133-
}
134-
}
109+
save_changes_lock: Arc<Mutex<()>>,
135110
}
136111

137112
impl MemoryStore {
@@ -348,6 +323,16 @@ impl CryptoStore for MemoryStore {
348323
settings.extend(changes.room_settings);
349324
}
350325

326+
if !changes.received_room_key_bundles.is_empty() {
327+
let mut room_key_bundles = self.room_key_bundles.write();
328+
for bundle in changes.received_room_key_bundles {
329+
room_key_bundles
330+
.entry(bundle.bundle_data.room_id.clone())
331+
.or_default()
332+
.insert(bundle.sender_user.clone(), bundle);
333+
}
334+
}
335+
351336
Ok(())
352337
}
353338

@@ -719,6 +704,18 @@ impl CryptoStore for MemoryStore {
719704
Ok(self.room_settings.read().get(room_id).cloned())
720705
}
721706

707+
async fn get_received_room_key_bundle_data(
708+
&self,
709+
room_id: &RoomId,
710+
user_id: &UserId,
711+
) -> Result<Option<StoredRoomKeyBundleData>> {
712+
let guard = self.room_key_bundles.read();
713+
714+
let result = guard.get(room_id).and_then(|bundles| bundles.get(user_id).cloned());
715+
716+
Ok(result)
717+
}
718+
722719
async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>> {
723720
Ok(self.custom_values.read().get(key).cloned())
724721
}
@@ -1249,7 +1246,7 @@ mod integration_tests {
12491246
},
12501247
store::{
12511248
BackupKeys, Changes, CryptoStore, DehydratedDeviceKey, PendingChanges, RoomKeyCounts,
1252-
RoomSettings,
1249+
RoomSettings, StoredRoomKeyBundleData,
12531250
},
12541251
types::events::room_key_withheld::RoomKeyWithheldEvent,
12551252
Account, DeviceData, GossipRequest, GossippedSecret, SecretInfo, Session, TrackedUser,
@@ -1511,6 +1508,14 @@ mod integration_tests {
15111508
self.0.get_room_settings(room_id).await
15121509
}
15131510

1511+
async fn get_received_room_key_bundle_data(
1512+
&self,
1513+
room_id: &RoomId,
1514+
user_id: &UserId,
1515+
) -> crate::store::Result<Option<StoredRoomKeyBundleData>, Self::Error> {
1516+
self.0.get_received_room_key_bundle_data(room_id, user_id).await
1517+
}
1518+
15141519
async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
15151520
self.0.get_custom_value(key).await
15161521
}

crates/matrix-sdk-crypto/src/store/mod.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ use crate::{
7070
identities::{user::UserIdentity, Device, DeviceData, UserDevices, UserIdentityData},
7171
olm::{
7272
Account, ExportedRoomKey, InboundGroupSession, OlmMessageHash, OutboundGroupSession,
73-
PrivateCrossSigningIdentity, Session, StaticAccountData,
73+
PrivateCrossSigningIdentity, SenderData, Session, StaticAccountData,
7474
},
7575
types::{
7676
events::room_key_withheld::RoomKeyWithheldEvent, BackupSecrets, CrossSigningSecrets,
@@ -101,7 +101,8 @@ pub use memorystore::MemoryStore;
101101
pub use traits::{CryptoStore, DynCryptoStore, IntoCryptoStore};
102102

103103
use crate::types::{
104-
events::room_key_withheld::RoomKeyWithheldContent, room_history::RoomKeyBundle,
104+
events::{room_key_bundle::RoomKeyBundleContent, room_key_withheld::RoomKeyWithheldContent},
105+
room_history::RoomKeyBundle,
105106
};
106107
pub use crate::{
107108
dehydrated_devices::DehydrationError,
@@ -541,6 +542,26 @@ pub struct Changes {
541542
pub room_settings: HashMap<OwnedRoomId, RoomSettings>,
542543
pub secrets: Vec<GossippedSecret>,
543544
pub next_batch_token: Option<String>,
545+
546+
/// Historical room key history bundles that we have received and should
547+
/// store.
548+
pub received_room_key_bundles: Vec<StoredRoomKeyBundleData>,
549+
}
550+
551+
/// Information about an [MSC4268] room key bundle.
552+
///
553+
/// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
554+
#[derive(Clone, Debug, Serialize, Deserialize)]
555+
pub struct StoredRoomKeyBundleData {
556+
/// The user that sent us this data.
557+
pub sender_user: OwnedUserId,
558+
559+
/// Information about the sender of this data and how much we trust that
560+
/// information.
561+
pub sender_data: SenderData,
562+
563+
/// The room key bundle data itself.
564+
pub bundle_data: RoomKeyBundleContent,
544565
}
545566

546567
/// A user for which we are tracking the list of devices.
@@ -573,6 +594,7 @@ impl Changes {
573594
&& self.room_settings.is_empty()
574595
&& self.secrets.is_empty()
575596
&& self.next_batch_token.is_none()
597+
&& self.received_room_key_bundles.is_empty()
576598
}
577599
}
578600

crates/matrix-sdk-crypto/src/store/traits.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use vodozemac::Curve25519PublicKey;
2323

2424
use super::{
2525
BackupKeys, Changes, CryptoStoreError, DehydratedDeviceKey, PendingChanges, Result,
26-
RoomKeyCounts, RoomSettings,
26+
RoomKeyCounts, RoomSettings, StoredRoomKeyBundleData,
2727
};
2828
#[cfg(doc)]
2929
use crate::olm::SenderData;
@@ -323,6 +323,14 @@ pub trait CryptoStore: AsyncTraitDeps {
323323
room_id: &RoomId,
324324
) -> Result<Option<RoomSettings>, Self::Error>;
325325

326+
/// Get the details about the room key bundle data received from the given
327+
/// user for the given room.
328+
async fn get_received_room_key_bundle_data(
329+
&self,
330+
room_id: &RoomId,
331+
user_id: &UserId,
332+
) -> Result<Option<StoredRoomKeyBundleData>, Self::Error>;
333+
326334
/// Get arbitrary data from the store
327335
///
328336
/// # Arguments
@@ -569,6 +577,14 @@ impl<T: CryptoStore> CryptoStore for EraseCryptoStoreError<T> {
569577
self.0.get_room_settings(room_id).await.map_err(Into::into)
570578
}
571579

580+
async fn get_received_room_key_bundle_data(
581+
&self,
582+
room_id: &RoomId,
583+
user_id: &UserId,
584+
) -> Result<Option<StoredRoomKeyBundleData>> {
585+
self.0.get_received_room_key_bundle_data(room_id, user_id).await.map_err(Into::into)
586+
}
587+
572588
async fn get_custom_value(&self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
573589
self.0.get_custom_value(key).await.map_err(Into::into)
574590
}

0 commit comments

Comments
 (0)