Skip to content

Commit b045462

Browse files
kaylendogrichvdh
authored andcommitted
feat: Append withheld info from room key bundle to store.
1 parent 8bb5e50 commit b045462

File tree

4 files changed

+186
-6
lines changed

4 files changed

+186
-6
lines changed

crates/matrix-sdk-crypto/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ All notable changes to this project will be documented in this file.
66

77
## [Unreleased] - ReleaseDate
88

9+
### Features
10+
11+
- Improve feedback support for shared history when downloading room key bundles.
12+
([#5737](https://github.com/matrix-org/matrix-rust-sdk/pull/5737))
13+
- Add `RoomKeyWithheldEntry` enum, wrapping either a received to-device `m.room_key.withheld` event or
14+
its content, if derived from a downloaded room key bundle.
15+
- `OlmMachine::receive_room_key_bundle` now appends withheld key information to the store.
16+
- [**breaking**] `Changes::withheld_session_info` now stores a `RoomKeyWithheldEntry` in each `room-id`-`session-id` entry.
17+
- [**breaking**] `CryptoStore::get_withheld_info` now returns `Result<Option<RoomKeyWithheldEntry>>`. This change also affects `MemoryStore`.
18+
919
### Bug Fixes
1020

1121
- Fix a bug introduced in 0.14.0 which meant that the serialization of the value returned by `OtherUserIdentity::verification_request_content` did not include a `msgtype` field.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,9 @@ impl OlmMachine {
10031003

10041004
if let RoomKeyWithheldContent::MegolmV1AesSha2(
10051005
MegolmV1AesSha2WithheldContent::BlackListed(c)
1006-
| MegolmV1AesSha2WithheldContent::Unverified(c),
1006+
| MegolmV1AesSha2WithheldContent::Unverified(c)
1007+
| MegolmV1AesSha2WithheldContent::Unauthorised(c)
1008+
| MegolmV1AesSha2WithheldContent::Unavailable(c),
10071009
) = &event.content
10081010
{
10091011
changes

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

Lines changed: 148 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use serde::{de::DeserializeOwned, Serialize};
5959
use thiserror::Error;
6060
use tokio::sync::{Mutex, Notify, OwnedRwLockWriteGuard, RwLock};
6161
use tokio_stream::wrappers::errors::BroadcastStreamRecvError;
62-
use tracing::{error, info, instrument, trace, warn};
62+
use tracing::{info, instrument, trace, warn};
6363
use types::{RoomKeyBundleInfo, StoredRoomKeyBundleData};
6464
use vodozemac::{megolm::SessionOrdering, Curve25519PublicKey};
6565

@@ -76,6 +76,7 @@ use crate::{
7676
Account, ExportedRoomKey, InboundGroupSession, PrivateCrossSigningIdentity, SenderData,
7777
Session, StaticAccountData,
7878
},
79+
store::types::RoomKeyWithheldEntry,
7980
types::{
8081
BackupSecrets, CrossSigningSecrets, MegolmBackupV1Curve25519AesSha2Secrets, RoomKeyExport,
8182
SecretsBundle,
@@ -1645,6 +1646,7 @@ impl Store {
16451646
}
16461647

16471648
self.import_room_key_bundle_sessions(bundle_info, &bundle, progress_listener).await?;
1649+
self.import_room_key_bundle_withheld_info(bundle_info, &bundle).await?;
16481650

16491651
Ok(())
16501652
}
@@ -1700,6 +1702,43 @@ impl Store {
17001702
Ok(())
17011703
}
17021704

1705+
async fn import_room_key_bundle_withheld_info(
1706+
&self,
1707+
bundle_info: &StoredRoomKeyBundleData,
1708+
bundle: &RoomKeyBundle,
1709+
) -> Result<(), CryptoStoreError> {
1710+
let mut changes = Changes::default();
1711+
for withheld in &bundle.withheld {
1712+
let (room_id, session_id) = match withheld {
1713+
RoomKeyWithheldContent::MegolmV1AesSha2(c) => match (c.room_id(), c.session_id()) {
1714+
(Some(room_id), Some(session_id)) => (room_id, session_id),
1715+
_ => continue,
1716+
},
1717+
#[cfg(feature = "experimental-algorithms")]
1718+
RoomKeyWithheldContent::MegolmV2AesSha2(c) => match (c.room_id(), c.session_id()) {
1719+
(Some(room_id), Some(session_id)) => (room_id, session_id),
1720+
_ => continue,
1721+
},
1722+
RoomKeyWithheldContent::Unknown(_) => continue,
1723+
};
1724+
1725+
if room_id != bundle_info.bundle_data.room_id {
1726+
trace!("Ignoring withheld info for incorrect room {} in bundle", room_id);
1727+
continue;
1728+
}
1729+
1730+
changes.withheld_session_info.entry(room_id.to_owned()).or_default().insert(
1731+
session_id.to_owned(),
1732+
RoomKeyWithheldEntry {
1733+
sender: bundle_info.sender_user.clone(),
1734+
content: withheld.to_owned(),
1735+
},
1736+
);
1737+
}
1738+
self.save_changes(changes).await?;
1739+
1740+
Ok(())
1741+
}
17031742
}
17041743

17051744
impl Deref for Store {
@@ -1735,16 +1774,25 @@ mod tests {
17351774
use futures_util::StreamExt;
17361775
use insta::{_macro_support::Content, assert_json_snapshot, internals::ContentPath};
17371776
use matrix_sdk_test::async_test;
1738-
use ruma::{device_id, owned_device_id, room_id, user_id, RoomId};
1777+
use ruma::{
1778+
device_id,
1779+
events::room::{EncryptedFileInit, JsonWebKeyInit},
1780+
owned_device_id, owned_mxc_uri, room_id,
1781+
serde::Base64,
1782+
user_id, RoomId,
1783+
};
17391784
use serde_json::json;
17401785
use vodozemac::megolm::SessionKey;
17411786

17421787
use crate::{
17431788
machine::test_helpers::get_machine_pair,
17441789
olm::{InboundGroupSession, SenderData},
1745-
store::types::{DehydratedDeviceKey, RoomKeyWithheldEntry},
1790+
store::types::{DehydratedDeviceKey, RoomKeyWithheldEntry, StoredRoomKeyBundleData},
17461791
types::{
1747-
events::room_key_withheld::{MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent},
1792+
events::{
1793+
room_key_bundle::RoomKeyBundleContent,
1794+
room_key_withheld::{MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent},
1795+
},
17481796
EventEncryptionAlgorithm,
17491797
},
17501798
OlmMachine,
@@ -2001,6 +2049,102 @@ mod tests {
20012049
});
20022050
}
20032051

2052+
#[async_test]
2053+
async fn test_receive_room_key_bundle() {
2054+
let alice = OlmMachine::new(user_id!("@a:s.co"), device_id!("ALICE")).await;
2055+
let alice_key = alice.identity_keys().curve25519;
2056+
let bob = OlmMachine::new(user_id!("@b:s.co"), device_id!("BOB")).await;
2057+
2058+
let room_id = room_id!("!room1:localhost");
2059+
2060+
let session_key1 = "AgAAAAC2XHVzsMBKs4QCRElJ92CJKyGtknCSC8HY7cQ7UYwndMKLQAejXLh5UA0l6s736mgctcUMNvELScUWrObdflrHo+vth/gWreXOaCnaSxmyjjKErQwyIYTkUfqbHy40RJfEesLwnN23on9XAkch/iy8R2+Jz7B8zfG01f2Ow2SxPQFnAndcO1ZSD2GmXgedy6n4B20MWI1jGP2wiexOWbFSya8DO/VxC9m5+/mF+WwYqdpKn9g4Y05Yw4uz7cdjTc3rXm7xK+8E7hI//5QD1nHPvuKYbjjM9u2JSL+Bzp61Cw";
2061+
let session_key2 = "AgAAAAC1BXreFTUQQSBGekTEuYxhdytRKyv4JgDGcG+VOBYdPNGgs807SdibCGJky4lJ3I+7ZDGHoUzZPZP/4ogGu4kxni0PWdtWuN7+5zsuamgoFF/BkaGeUUGv6kgIkx8pyPpM5SASTUEP9bN2loDSpUPYwfiIqz74DgC4WQ4435sTBctYvKz8n+TDJwdLXpyT6zKljuqADAioud+s/iqx9LYn9HpbBfezZcvbg67GtE113pLrvde3IcPI5s6dNHK2onGO2B2eoaobcen18bbEDnlUGPeIivArLya7Da6us14jBQ";
2062+
2063+
let sessions = [
2064+
create_inbound_group_session_with_visibility(
2065+
&alice,
2066+
room_id,
2067+
&SessionKey::from_base64(session_key1).unwrap(),
2068+
true,
2069+
),
2070+
create_inbound_group_session_with_visibility(
2071+
&alice,
2072+
room_id,
2073+
&SessionKey::from_base64(session_key2).unwrap(),
2074+
false,
2075+
),
2076+
];
2077+
2078+
alice.store().save_inbound_group_sessions(&sessions).await.unwrap();
2079+
let bundle = alice.store().build_room_key_bundle(room_id).await.unwrap();
2080+
2081+
bob.store()
2082+
.receive_room_key_bundle(
2083+
&StoredRoomKeyBundleData {
2084+
sender_user: alice.user_id().to_owned(),
2085+
sender_key: alice_key,
2086+
sender_data: SenderData::sender_verified(
2087+
alice.user_id(),
2088+
device_id!("ALICE"),
2089+
alice.identity_keys().ed25519,
2090+
),
2091+
2092+
bundle_data: RoomKeyBundleContent {
2093+
room_id: room_id.to_owned(),
2094+
// This isn't used at all in the method call, so we can fill it with
2095+
// garbage.
2096+
file: EncryptedFileInit {
2097+
url: owned_mxc_uri!("mxc://example.com/0"),
2098+
key: JsonWebKeyInit {
2099+
kty: "oct".to_owned(),
2100+
key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()],
2101+
alg: "A256CTR.".to_owned(),
2102+
k: Base64::new(vec![0u8; 128]),
2103+
ext: true,
2104+
}
2105+
.into(),
2106+
iv: Base64::new(vec![0u8; 128]),
2107+
hashes: vec![("sha256".to_owned(), Base64::new(vec![0u8; 128]))]
2108+
.into_iter()
2109+
.collect(),
2110+
v: "v2".to_owned(),
2111+
}
2112+
.into(),
2113+
},
2114+
},
2115+
bundle,
2116+
|_, _| {},
2117+
)
2118+
.await
2119+
.unwrap();
2120+
2121+
// The room key should be imported successfully
2122+
let imported_sessions =
2123+
bob.store().get_inbound_group_sessions_by_room_id(room_id).await.unwrap();
2124+
2125+
assert_eq!(imported_sessions.len(), 1);
2126+
assert_eq!(imported_sessions[0].room_id(), room_id);
2127+
2128+
assert_matches!(
2129+
bob.store()
2130+
.get_withheld_info(room_id, sessions[1].session_id())
2131+
.await
2132+
.unwrap()
2133+
.expect("Withheld info should be present in the store."),
2134+
RoomKeyWithheldEntry {
2135+
#[cfg(not(feature = "experimental-algorithms"))]
2136+
content: RoomKeyWithheldContent::MegolmV1AesSha2(
2137+
MegolmV1AesSha2WithheldContent::Unauthorised(_)
2138+
),
2139+
#[cfg(feature = "experimental-algorithms")]
2140+
content: RoomKeyWithheldContent::MegolmV2AesSha2(
2141+
MegolmV1AesSha2WithheldContent::Unauthorised(_)
2142+
),
2143+
..
2144+
}
2145+
);
2146+
}
2147+
20042148
/// Tests that the new store format introduced in [#5737][#5737] does not
20052149
/// conflict with items already in the store that were serialised with the
20062150
/// older format.

crates/matrix-sdk-crypto/src/types/events/room_key_withheld.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
use std::collections::BTreeMap;
1818

1919
use matrix_sdk_common::deserialized_responses::WithheldCode;
20-
use ruma::{events::AnyToDeviceEventContent, serde::JsonCastable, OwnedDeviceId, OwnedRoomId};
20+
use ruma::{
21+
events::AnyToDeviceEventContent, serde::JsonCastable, OwnedDeviceId, OwnedRoomId, RoomId,
22+
};
2123
use serde::{Deserialize, Serialize};
2224
use serde_json::Value;
2325
use vodozemac::Curve25519PublicKey;
@@ -223,6 +225,28 @@ impl CommonWithheldCodeContent {
223225
}
224226

225227
impl MegolmV1AesSha2WithheldContent {
228+
/// Get the session ID for this content, if available.
229+
pub fn session_id(&self) -> Option<&str> {
230+
match self {
231+
MegolmV1AesSha2WithheldContent::BlackListed(content)
232+
| MegolmV1AesSha2WithheldContent::Unverified(content)
233+
| MegolmV1AesSha2WithheldContent::Unauthorised(content)
234+
| MegolmV1AesSha2WithheldContent::Unavailable(content) => Some(&content.session_id),
235+
MegolmV1AesSha2WithheldContent::NoOlm(_) => None,
236+
}
237+
}
238+
239+
/// Get the room ID for this content, if available.
240+
pub fn room_id(&self) -> Option<&RoomId> {
241+
match self {
242+
MegolmV1AesSha2WithheldContent::BlackListed(content)
243+
| MegolmV1AesSha2WithheldContent::Unverified(content)
244+
| MegolmV1AesSha2WithheldContent::Unauthorised(content)
245+
| MegolmV1AesSha2WithheldContent::Unavailable(content) => Some(&content.room_id),
246+
MegolmV1AesSha2WithheldContent::NoOlm(_) => None,
247+
}
248+
}
249+
226250
/// Get the withheld code for this content
227251
pub fn withheld_code(&self) -> WithheldCode {
228252
match self {

0 commit comments

Comments
 (0)