Skip to content

Commit bfa89bc

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 e1d05fa commit bfa89bc

File tree

15 files changed

+143
-5
lines changed

15 files changed

+143
-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

+44-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,13 @@ 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 users who are
159+
/// invited to the room in the future, allowing access to history, as
160+
/// defined in [MSC3061].
161+
///
162+
/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
163+
shared_history: bool,
157164
}
158165

159166
impl InboundGroupSession {
@@ -176,6 +183,7 @@ impl InboundGroupSession {
176183
///
177184
/// * `sender_data` - Information about the sender of the to-device message
178185
/// that established this session.
186+
#[allow(clippy::too_many_arguments)]
179187
pub fn new(
180188
sender_key: Curve25519PublicKey,
181189
signing_key: Ed25519PublicKey,
@@ -184,6 +192,7 @@ impl InboundGroupSession {
184192
sender_data: SenderData,
185193
encryption_algorithm: EventEncryptionAlgorithm,
186194
history_visibility: Option<HistoryVisibility>,
195+
shared_history: bool,
187196
) -> Result<Self, SessionCreationError> {
188197
let config = OutboundGroupSession::session_config(&encryption_algorithm)?;
189198

@@ -208,6 +217,7 @@ impl InboundGroupSession {
208217
imported: false,
209218
algorithm: encryption_algorithm.into(),
210219
backed_up: AtomicBool::new(false).into(),
220+
shared_history,
211221
})
212222
}
213223

@@ -228,7 +238,13 @@ impl InboundGroupSession {
228238
signing_key: Ed25519PublicKey,
229239
content: &room_key::MegolmV1AesSha2Content,
230240
) -> Result<Self, SessionCreationError> {
231-
let room_key::MegolmV1AesSha2Content { room_id, session_id: _, session_key, .. } = content;
241+
let room_key::MegolmV1AesSha2Content {
242+
room_id,
243+
session_id: _,
244+
session_key,
245+
shared_history,
246+
..
247+
} = content;
232248

233249
Self::new(
234250
sender_key,
@@ -238,6 +254,7 @@ impl InboundGroupSession {
238254
SenderData::unknown(),
239255
EventEncryptionAlgorithm::MegolmV1AesSha2,
240256
None,
257+
*shared_history,
241258
)
242259
}
243260

@@ -265,6 +282,7 @@ impl InboundGroupSession {
265282
backed_up: self.backed_up(),
266283
history_visibility: self.history_visibility.as_ref().clone(),
267284
algorithm: (*self.algorithm).to_owned(),
285+
shared_history: self.shared_history,
268286
}
269287
}
270288

@@ -317,6 +335,7 @@ impl InboundGroupSession {
317335
forwarding_curve25519_key_chain: vec![],
318336
sender_claimed_keys: (*self.creator_info.signing_keys).clone(),
319337
session_key,
338+
shared_history: self.shared_history,
320339
}
321340
}
322341

@@ -342,6 +361,7 @@ impl InboundGroupSession {
342361
backed_up,
343362
history_visibility,
344363
algorithm,
364+
shared_history,
345365
} = pickle;
346366

347367
let session: InnerSession = pickle.into();
@@ -362,6 +382,7 @@ impl InboundGroupSession {
362382
backed_up: AtomicBool::from(backed_up).into(),
363383
algorithm: algorithm.into(),
364384
imported,
385+
shared_history,
365386
})
366387
}
367388

@@ -511,6 +532,15 @@ impl InboundGroupSession {
511532
pub fn sender_data_type(&self) -> SenderDataType {
512533
self.sender_data.to_type()
513534
}
535+
536+
/// Whether this [`InboundGroupSession`] can be shared with users who are
537+
/// invited to the room in the future, allowing access to history, as
538+
/// defined in [MSC3061].
539+
///
540+
/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
541+
pub fn shared_history(&self) -> bool {
542+
self.shared_history
543+
}
514544
}
515545

516546
#[cfg(not(tarpaulin_include))]
@@ -556,6 +586,13 @@ pub struct PickledInboundGroupSession {
556586
/// The algorithm of this inbound group session.
557587
#[serde(default = "default_algorithm")]
558588
pub algorithm: EventEncryptionAlgorithm,
589+
/// Whether this [`InboundGroupSession`] can be shared with users who are
590+
/// invited to the room in the future, allowing access to history, as
591+
/// defined in [MSC3061].
592+
///
593+
/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
594+
#[serde(default)]
595+
pub shared_history: bool,
559596
}
560597

561598
fn default_algorithm() -> EventEncryptionAlgorithm {
@@ -574,6 +611,7 @@ impl TryFrom<&ExportedRoomKey> for InboundGroupSession {
574611
session_key,
575612
sender_claimed_keys,
576613
forwarding_curve25519_key_chain: _,
614+
shared_history,
577615
} = key;
578616

579617
let config = OutboundGroupSession::session_config(algorithm)?;
@@ -596,6 +634,7 @@ impl TryFrom<&ExportedRoomKey> for InboundGroupSession {
596634
imported: true,
597635
algorithm: algorithm.to_owned().into(),
598636
backed_up: AtomicBool::from(false).into(),
637+
shared_history: *shared_history,
599638
})
600639
}
601640
}
@@ -626,6 +665,7 @@ impl From<&ForwardedMegolmV1AesSha2Content> for InboundGroupSession {
626665
imported: true,
627666
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2.into(),
628667
backed_up: AtomicBool::from(false).into(),
668+
shared_history: false,
629669
}
630670
}
631671
}
@@ -652,6 +692,7 @@ impl From<&ForwardedMegolmV2AesSha2Content> for InboundGroupSession {
652692
imported: true,
653693
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2.into(),
654694
backed_up: AtomicBool::from(false).into(),
695+
shared_history: false,
655696
}
656697
}
657698
}
@@ -768,6 +809,7 @@ mod tests {
768809
SenderData::unknown(),
769810
EventEncryptionAlgorithm::MegolmV1AesSha2,
770811
Some(HistoryVisibility::Shared),
812+
false,
771813
)
772814
.unwrap();
773815

@@ -814,6 +856,7 @@ mod tests {
814856
"room_id":"!test:localhost",
815857
"imported":false,
816858
"backed_up":false,
859+
"shared_history":false,
817860
"history_visibility":"shared",
818861
"algorithm":"m.megolm.v1.aes-sha2"
819862
})

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

+23-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ 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 users who are
103+
/// invited to the room in the future, allowing access to history, as
104+
/// defined in [MSC3061].
105+
///
106+
/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
107+
#[serde(default, rename = "org.matrix.msc3061.shared_history")]
108+
pub shared_history: bool,
101109
}
102110

103111
impl ExportedRoomKey {
@@ -115,6 +123,7 @@ impl ExportedRoomKey {
115123
session_key,
116124
sender_claimed_keys,
117125
forwarding_curve25519_key_chain,
126+
shared_history,
118127
} = room_key;
119128

120129
Self {
@@ -125,6 +134,7 @@ impl ExportedRoomKey {
125134
session_key,
126135
sender_claimed_keys,
127136
forwarding_curve25519_key_chain,
137+
shared_history,
128138
}
129139
}
130140
}
@@ -152,13 +162,21 @@ pub struct BackedUpRoomKey {
152162
pub sender_claimed_keys: SigningKeys<DeviceKeyAlgorithm>,
153163

154164
/// Chain of Curve25519 keys through which this session was forwarded, via
155-
/// m.forwarded_room_key events.
165+
/// `m.forwarded_room_key` events.
156166
#[serde(
157167
default,
158168
deserialize_with = "deserialize_curve_key_vec",
159169
serialize_with = "serialize_curve_key_vec"
160170
)]
161171
pub forwarding_curve25519_key_chain: Vec<Curve25519PublicKey>,
172+
173+
/// Whether this [`BackedUpRoomKey`] can be shared with users who are
174+
/// invited to the room in the future, allowing access to history, as
175+
/// defined in [MSC3061].
176+
///
177+
/// [MSC3061]: https://github.com/matrix-org/matrix-spec-proposals/pull/3061
178+
#[serde(default, rename = "org.matrix.msc3061.shared_history")]
179+
pub shared_history: bool,
162180
}
163181

164182
impl TryFrom<ExportedRoomKey> for ForwardedRoomKeyContent {
@@ -228,6 +246,7 @@ impl From<ExportedRoomKey> for BackedUpRoomKey {
228246
session_key,
229247
sender_claimed_keys,
230248
forwarding_curve25519_key_chain,
249+
shared_history,
231250
} = value;
232251

233252
Self {
@@ -236,6 +255,7 @@ impl From<ExportedRoomKey> for BackedUpRoomKey {
236255
session_key,
237256
sender_claimed_keys,
238257
forwarding_curve25519_key_chain,
258+
shared_history,
239259
}
240260
}
241261
}
@@ -261,6 +281,7 @@ impl TryFrom<ForwardedRoomKeyContent> for ExportedRoomKey {
261281
sender_claimed_keys,
262282
sender_key: content.claimed_sender_key,
263283
session_key: content.session_key,
284+
shared_history: false,
264285
})
265286
}
266287
#[cfg(feature = "experimental-algorithms")]
@@ -272,6 +293,7 @@ impl TryFrom<ForwardedRoomKeyContent> for ExportedRoomKey {
272293
sender_claimed_keys: content.claimed_signing_keys,
273294
sender_key: content.claimed_sender_key,
274295
session_key: content.session_key,
296+
shared_history: false,
275297
}),
276298
ForwardedRoomKeyContent::Unknown(c) => Err(SessionExportError::Algorithm(c.algorithm)),
277299
}

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
}

0 commit comments

Comments
 (0)