Skip to content

Commit 0fa6b46

Browse files
committed
feat(crypto): Add support for the shared_history flag defined in MSC3061
This patch adds support for the `shared_history` flag from MSC3061 to the `m.room_key` content, exported room keys, and backed-up room keys. The flag is now persisted in our `InboundGroupSession`. Additionally, when creating a new `InboundGroupSession`, we ensure the `shared_history` flag is set appropriately. MSC3061: matrix-org/matrix-spec-proposals#3061
1 parent b8783c2 commit 0fa6b46

File tree

14 files changed

+131
-5
lines changed

14 files changed

+131
-5
lines changed

bindings/matrix-sdk-crypto-ffi/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ fn collect_sessions(
507507
imported: session.imported,
508508
backed_up: session.backed_up,
509509
history_visibility: None,
510+
shared_history: false,
510511
algorithm: RustEventEncryptionAlgorithm::MegolmV1AesSha2,
511512
};
512513

crates/matrix-sdk-crypto/CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ All notable changes to this project will be documented in this file.
66

77
## [Unreleased] - ReleaseDate
88

9+
### Features
10+
11+
- [**breaking**] Add support for the shared history flag defined in [MSC3061](https://github.com/matrix-org/matrix-spec-proposals/pull/3061).
12+
The shared history flag is now respected when room keys are received as an
13+
`m.room_key` event as well as when they are imported from a backup or a file
14+
export. We also ensure to set the flag when we send out room keys. Due to
15+
this, a new argument to the constructor for `room_key::MegolmV1AesSha2Content`
16+
has been added and `PickledInboundGroupSession` has received a new
17+
`shared_history` field that defaults to `false.`
18+
([#4700](https://github.com/matrix-org/matrix-rust-sdk/pull/4700))
19+
920
## [0.10.0] - 2025-02-04
1021

1122
### Features

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

+2
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ async fn test_verification_states_multiple_device() {
353353
SenderData::unknown(),
354354
EventEncryptionAlgorithm::MegolmV1AesSha2,
355355
None,
356+
false,
356357
)
357358
.unwrap();
358359

@@ -370,6 +371,7 @@ async fn test_verification_states_multiple_device() {
370371
SenderData::unknown(),
371372
EventEncryptionAlgorithm::MegolmV1AesSha2,
372373
None,
374+
false,
373375
)
374376
.unwrap();
375377

crates/matrix-sdk-crypto/src/olm/account.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use ruma::{
3232
upload_signatures::v3::{Request as SignatureUploadRequest, SignedKeys},
3333
},
3434
},
35-
events::AnyToDeviceEvent,
35+
events::{room::history_visibility::HistoryVisibility, AnyToDeviceEvent},
3636
serde::Raw,
3737
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm,
3838
OneTimeKeyId, OwnedDeviceId, OwnedDeviceKeyId, OwnedOneTimeKeyId, OwnedUserId, RoomId,
@@ -220,6 +220,7 @@ impl StaticAccountData {
220220

221221
let sender_key = identity_keys.curve25519;
222222
let signing_key = identity_keys.ed25519;
223+
let shared_history = shared_history_from_history_visibility(&visibility);
223224

224225
let inbound = InboundGroupSession::new(
225226
sender_key,
@@ -229,6 +230,7 @@ impl StaticAccountData {
229230
own_sender_data,
230231
algorithm,
231232
Some(visibility),
233+
shared_history,
232234
)?;
233235

234236
Ok((outbound, inbound))
@@ -1511,6 +1513,34 @@ impl PartialEq for Account {
15111513
}
15121514
}
15131515

1516+
/// Calculate the shared history flag from the history visibility as defined in
1517+
/// [MSC3061]
1518+
///
1519+
/// The MSC defines that the shared history flag should be set to true when the
1520+
/// history visibility setting is set to `shared` or `world_readable`:
1521+
///
1522+
/// > A room key is flagged as having been used for shared history when it was
1523+
/// > used to encrypt a message while the room's history visibility setting
1524+
/// > was set to world_readable or shared.
1525+
///
1526+
/// In all other cases, even if we encounter a custom history visibility, we
1527+
/// should return false:
1528+
///
1529+
/// > If the client does not have an m.room.history_visibility state event for
1530+
/// > the room, or its value is not understood, the client should treat it as if
1531+
/// > its value is joined for the purposes of determining whether the key is
1532+
/// > used for shared history.
1533+
///
1534+
/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
1535+
pub(crate) fn shared_history_from_history_visibility(
1536+
history_visibility: &HistoryVisibility,
1537+
) -> bool {
1538+
match history_visibility {
1539+
HistoryVisibility::Shared | HistoryVisibility::WorldReadable => true,
1540+
HistoryVisibility::Invited | HistoryVisibility::Joined | _ => false,
1541+
}
1542+
}
1543+
15141544
/// Expand the pickle key for an older version of dehydrated devices
15151545
///
15161546
/// The `org.matrix.msc3814.v1.olm` variant of dehydrated devices used the

crates/matrix-sdk-crypto/src/olm/group_sessions/inbound.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ pub struct InboundGroupSession {
154154

155155
/// Was this room key backed up to the server.
156156
backed_up: Arc<AtomicBool>,
157+
158+
/// Whether this [`InboundGroupSession`] can be shared with newly joined
159+
/// users, allowing access to history, as defined in [MSC3061].
160+
///
161+
/// MSC3061: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
162+
shared_history: bool,
157163
}
158164

159165
impl InboundGroupSession {
@@ -176,6 +182,7 @@ impl InboundGroupSession {
176182
///
177183
/// * `sender_data` - Information about the sender of the to-device message
178184
/// that established this session.
185+
#[allow(clippy::too_many_arguments)]
179186
pub fn new(
180187
sender_key: Curve25519PublicKey,
181188
signing_key: Ed25519PublicKey,
@@ -184,6 +191,7 @@ impl InboundGroupSession {
184191
sender_data: SenderData,
185192
encryption_algorithm: EventEncryptionAlgorithm,
186193
history_visibility: Option<HistoryVisibility>,
194+
shared_history: bool,
187195
) -> Result<Self, SessionCreationError> {
188196
let config = OutboundGroupSession::session_config(&encryption_algorithm)?;
189197

@@ -208,6 +216,7 @@ impl InboundGroupSession {
208216
imported: false,
209217
algorithm: encryption_algorithm.into(),
210218
backed_up: AtomicBool::new(false).into(),
219+
shared_history,
211220
})
212221
}
213222

@@ -228,7 +237,13 @@ impl InboundGroupSession {
228237
signing_key: Ed25519PublicKey,
229238
content: &room_key::MegolmV1AesSha2Content,
230239
) -> Result<Self, SessionCreationError> {
231-
let room_key::MegolmV1AesSha2Content { room_id, session_id: _, session_key, .. } = content;
240+
let room_key::MegolmV1AesSha2Content {
241+
room_id,
242+
session_id: _,
243+
session_key,
244+
shared_history,
245+
..
246+
} = content;
232247

233248
Self::new(
234249
sender_key,
@@ -238,6 +253,7 @@ impl InboundGroupSession {
238253
SenderData::unknown(),
239254
EventEncryptionAlgorithm::MegolmV1AesSha2,
240255
None,
256+
*shared_history,
241257
)
242258
}
243259

@@ -265,6 +281,7 @@ impl InboundGroupSession {
265281
backed_up: self.backed_up(),
266282
history_visibility: self.history_visibility.as_ref().clone(),
267283
algorithm: (*self.algorithm).to_owned(),
284+
shared_history: self.shared_history,
268285
}
269286
}
270287

@@ -317,6 +334,7 @@ impl InboundGroupSession {
317334
forwarding_curve25519_key_chain: vec![],
318335
sender_claimed_keys: (*self.creator_info.signing_keys).clone(),
319336
session_key,
337+
shared_history: self.shared_history,
320338
}
321339
}
322340

@@ -342,6 +360,7 @@ impl InboundGroupSession {
342360
backed_up,
343361
history_visibility,
344362
algorithm,
363+
shared_history,
345364
} = pickle;
346365

347366
let session: InnerSession = pickle.into();
@@ -362,6 +381,7 @@ impl InboundGroupSession {
362381
backed_up: AtomicBool::from(backed_up).into(),
363382
algorithm: algorithm.into(),
364383
imported,
384+
shared_history,
365385
})
366386
}
367387

@@ -511,6 +531,14 @@ impl InboundGroupSession {
511531
pub fn sender_data_type(&self) -> SenderDataType {
512532
self.sender_data.to_type()
513533
}
534+
535+
/// Whether this [`InboundGroupSession`] can be shared with newly joined
536+
/// users, allowing access to history, as defined in [MSC3061].
537+
///
538+
/// MSC3061: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
539+
pub fn shared_history(&self) -> bool {
540+
self.shared_history
541+
}
514542
}
515543

516544
#[cfg(not(tarpaulin_include))]
@@ -556,6 +584,12 @@ pub struct PickledInboundGroupSession {
556584
/// The algorithm of this inbound group session.
557585
#[serde(default = "default_algorithm")]
558586
pub algorithm: EventEncryptionAlgorithm,
587+
/// Whether this [`InboundGroupSession`] can be shared with newly joined
588+
/// users, allowing access to history, as defined in [MSC3061].
589+
///
590+
/// MSC3061: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
591+
#[serde(default)]
592+
pub shared_history: bool,
559593
}
560594

561595
fn default_algorithm() -> EventEncryptionAlgorithm {
@@ -574,6 +608,7 @@ impl TryFrom<&ExportedRoomKey> for InboundGroupSession {
574608
session_key,
575609
sender_claimed_keys,
576610
forwarding_curve25519_key_chain: _,
611+
shared_history,
577612
} = key;
578613

579614
let config = OutboundGroupSession::session_config(&key.algorithm)?;
@@ -596,6 +631,7 @@ impl TryFrom<&ExportedRoomKey> for InboundGroupSession {
596631
imported: true,
597632
algorithm: algorithm.to_owned().into(),
598633
backed_up: AtomicBool::from(false).into(),
634+
shared_history: *shared_history,
599635
})
600636
}
601637
}
@@ -626,6 +662,7 @@ impl From<&ForwardedMegolmV1AesSha2Content> for InboundGroupSession {
626662
imported: true,
627663
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2.into(),
628664
backed_up: AtomicBool::from(false).into(),
665+
shared_history: false,
629666
}
630667
}
631668
}
@@ -652,6 +689,7 @@ impl From<&ForwardedMegolmV2AesSha2Content> for InboundGroupSession {
652689
imported: true,
653690
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2.into(),
654691
backed_up: AtomicBool::from(false).into(),
692+
shared_history: false,
655693
}
656694
}
657695
}
@@ -768,6 +806,7 @@ mod tests {
768806
SenderData::unknown(),
769807
EventEncryptionAlgorithm::MegolmV1AesSha2,
770808
Some(HistoryVisibility::Shared),
809+
false,
771810
)
772811
.unwrap();
773812

crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ pub struct ExportedRoomKey {
9898
serialize_with = "serialize_curve_key_vec"
9999
)]
100100
pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
101+
102+
/// Whether this [`ExportedRoomKey`] can be shared with newly joined
103+
/// users, allowing access to history, as defined in [MSC3061].
104+
///
105+
/// MSC3061: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
106+
#[serde(default, rename = "org.matrix.msc3061.shared_history")]
107+
pub shared_history: bool,
101108
}
102109

103110
impl ExportedRoomKey {
@@ -115,6 +122,7 @@ impl ExportedRoomKey {
115122
session_key,
116123
sender_claimed_keys,
117124
forwarding_curve25519_key_chain,
125+
shared_history,
118126
} = room_key;
119127

120128
Self {
@@ -125,6 +133,7 @@ impl ExportedRoomKey {
125133
session_key,
126134
sender_claimed_keys,
127135
forwarding_curve25519_key_chain,
136+
shared_history,
128137
}
129138
}
130139
}
@@ -152,13 +161,20 @@ pub struct BackedUpRoomKey {
152161
pub sender_claimed_keys: SigningKeys<DeviceKeyAlgorithm>,
153162

154163
/// Chain of Curve25519 keys through which this session was forwarded, via
155-
/// m.forwarded_room_key events.
164+
/// `m.forwarded_room_key` events.
156165
#[serde(
157166
default,
158167
deserialize_with = "deserialize_curve_key_vec",
159168
serialize_with = "serialize_curve_key_vec"
160169
)]
161170
pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
171+
172+
/// Whether this [`BackedUpRoomKey`] can be shared with newly joined
173+
/// users, allowing access to history, as defined in [MSC3061].
174+
///
175+
/// MSC3061: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
176+
#[serde(default, rename = "org.matrix.msc3061.shared_history")]
177+
pub shared_history: bool,
162178
}
163179

164180
impl TryFrom<ExportedRoomKey> for ForwardedRoomKeyContent {
@@ -228,6 +244,7 @@ impl From<ExportedRoomKey> for BackedUpRoomKey {
228244
session_key,
229245
sender_claimed_keys,
230246
forwarding_curve25519_key_chain,
247+
shared_history,
231248
} = value;
232249

233250
Self {
@@ -236,6 +253,7 @@ impl From<ExportedRoomKey> for BackedUpRoomKey {
236253
session_key,
237254
sender_claimed_keys,
238255
forwarding_curve25519_key_chain,
256+
shared_history,
239257
}
240258
}
241259
}
@@ -261,6 +279,7 @@ impl TryFrom<ForwardedRoomKeyContent> for ExportedRoomKey {
261279
sender_claimed_keys,
262280
sender_key: content.claimed_sender_key,
263281
session_key: content.session_key,
282+
shared_history: false,
264283
})
265284
}
266285
#[cfg(feature = "experimental-algorithms")]

crates/matrix-sdk-crypto/src/olm/group_sessions/outbound.rs

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use super::SessionCreationError;
4747
#[cfg(feature = "experimental-algorithms")]
4848
use crate::types::events::room::encrypted::MegolmV2AesSha2Content;
4949
use crate::{
50+
olm::account::shared_history_from_history_visibility,
5051
session_manager::CollectStrategy,
5152
store::caches::SequenceNumber,
5253
types::{
@@ -526,12 +527,15 @@ impl OutboundGroupSession {
526527

527528
pub(crate) async fn as_content(&self) -> RoomKeyContent {
528529
let session_key = self.session_key().await;
530+
let shared_history =
531+
shared_history_from_history_visibility(&self.settings.history_visibility);
529532

530533
RoomKeyContent::MegolmV1AesSha2(
531534
MegolmV1AesSha2RoomKeyContent::new(
532535
self.room_id().to_owned(),
533536
self.session_id().to_owned(),
534537
session_key,
538+
shared_history,
535539
)
536540
.into(),
537541
)

crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data_finder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,7 @@ mod tests {
861861
SenderData::unknown(),
862862
EventEncryptionAlgorithm::MegolmV1AesSha2,
863863
None,
864+
false,
864865
)
865866
.unwrap();
866867
if options.session_is_imported {
@@ -1083,6 +1084,7 @@ mod tests {
10831084
room_id.to_owned(),
10841085
"mysession".to_owned(),
10851086
clone_session_key(session_key),
1087+
false,
10861088
))),
10871089
)
10881090
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ macro_rules! cryptostore_integration_tests {
13081308
sender_data,
13091309
EventEncryptionAlgorithm::MegolmV1AesSha2,
13101310
None,
1311+
false,
13111312
)
13121313
.unwrap()
13131314
}

0 commit comments

Comments
 (0)