Skip to content

Commit 88a8a70

Browse files
authored
Merge pull request #3200 from matrix-org/andybalaam/feature-flag-for-overriding-expiration-min
crypto: Add a feature flag to disable the minimum session rotation time
2 parents 3fb8a46 + ff1555e commit 88a8a70

File tree

3 files changed

+75
-12
lines changed

3 files changed

+75
-12
lines changed

crates/matrix-sdk-crypto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ qrcode = ["dep:matrix-sdk-qrcode"]
2222
message-ids = ["dep:ulid"]
2323
experimental-algorithms = []
2424
uniffi = ["dep:uniffi"]
25+
_disable-minimum-rotation-period-ms = []
2526

2627
# Testing helpers for implementations based upon this
2728
testing = ["dep:http"]

crates/matrix-sdk-crypto/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,6 @@ The following crate feature flags are available:
7070

7171
* `qrcode`: Enbles QRcode generation and reading code
7272

73-
* `testing`: provides facilities and functions for tests, in particular for integration testing store implementations. ATTENTION: do not ever use outside of tests, we do not provide any stability warantees on these, these are merely helpers. If you find you _need_ any function provided here outside of tests, please open a Github Issue and inform us about your use case for us to consider.
73+
* `testing`: Provides facilities and functions for tests, in particular for integration testing store implementations. ATTENTION: do not ever use outside of tests, we do not provide any stability warantees on these, these are merely helpers. If you find you _need_ any function provided here outside of tests, please open a Github Issue and inform us about your use case for us to consider.
74+
75+
* `_disable-minimum-rotation-period-ms`: Do not use except for testing. Disables the floor on the rotation period of room keys.

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

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ use crate::{
5959
ReadOnlyDevice, ToDeviceRequest,
6060
};
6161

62-
const ROTATION_PERIOD: Duration = Duration::from_millis(604800000);
62+
const ONE_HOUR: Duration = Duration::from_secs(60 * 60);
63+
const ONE_WEEK: Duration = Duration::from_secs(60 * 60 * 24 * 7);
64+
65+
const ROTATION_PERIOD: Duration = ONE_WEEK;
6366
const ROTATION_MESSAGES: u64 = 100;
6467

6568
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -415,15 +418,27 @@ impl OutboundGroupSession {
415418
fn elapsed(&self) -> bool {
416419
let creation_time = Duration::from_secs(self.creation_time.get().into());
417420
let now = Duration::from_secs(SecondsSinceUnixEpoch::now().get().into());
418-
419-
// Since the encryption settings are provided by users and not
420-
// checked someone could set a really low rotation period so
421-
// clamp it to an hour.
422421
now.checked_sub(creation_time)
423-
.map(|elapsed| elapsed >= max(self.settings.rotation_period, Duration::from_secs(3600)))
422+
.map(|elapsed| elapsed >= self.safe_rotation_period())
424423
.unwrap_or(true)
425424
}
426425

426+
/// Returns the rotation_period_ms that was set for this session, clamped
427+
/// to be no less than one hour.
428+
///
429+
/// This is to prevent a malicious or careless user causing sessions to be
430+
/// rotated very frequently.
431+
///
432+
/// The feature flag `_disable-minimum-rotation-period-ms` can
433+
/// be used to prevent this behaviour (which can be useful for tests).
434+
fn safe_rotation_period(&self) -> Duration {
435+
if cfg!(feature = "_disable-minimum-rotation-period-ms") {
436+
self.settings.rotation_period
437+
} else {
438+
max(self.settings.rotation_period, ONE_HOUR)
439+
}
440+
}
441+
427442
/// Check if the session has expired and if it should be rotated.
428443
///
429444
/// A session will expire after some time or if enough messages have been
@@ -776,6 +791,8 @@ mod tests {
776791

777792
use crate::{olm::OutboundGroupSession, Account, EncryptionSettings, MegolmError};
778793

794+
const TWO_HOURS: Duration = Duration::from_secs(60 * 60 * 2);
795+
779796
#[async_test]
780797
async fn session_is_not_expired_if_no_messages_sent_and_no_time_passed() {
781798
// Given a session that expires after one message
@@ -816,10 +833,10 @@ mod tests {
816833
}
817834

818835
#[async_test]
819-
async fn session_with_short_rotation_period_is_not_expired_after_no_time() {
820-
// Given a session with a 100ms expiration
836+
async fn session_with_rotation_period_is_not_expired_after_no_time() {
837+
// Given a session with a 2h expiration
821838
let session = create_session(EncryptionSettings {
822-
rotation_period: Duration::from_millis(100),
839+
rotation_period: TWO_HOURS,
823840
..Default::default()
824841
})
825842
.await;
@@ -832,21 +849,64 @@ mod tests {
832849

833850
#[async_test]
834851
async fn session_is_expired_after_rotation_period() {
852+
// Given a session with a 2h expiration
853+
let mut session = create_session(EncryptionSettings {
854+
rotation_period: TWO_HOURS,
855+
..Default::default()
856+
})
857+
.await;
858+
859+
// When 3 hours have passed
860+
let now = SecondsSinceUnixEpoch::now();
861+
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(10800));
862+
863+
// Then the session is expired
864+
assert!(session.expired());
865+
}
866+
867+
#[async_test]
868+
#[cfg(not(feature = "_disable-minimum-rotation-period-ms"))]
869+
async fn session_does_not_expire_under_one_hour_even_if_we_ask_for_shorter() {
835870
// Given a session with a 100ms expiration
836871
let mut session = create_session(EncryptionSettings {
837872
rotation_period: Duration::from_millis(100),
838873
..Default::default()
839874
})
840875
.await;
841876

842-
// When one hour has passed
877+
// When less than an hour has passed
843878
let now = SecondsSinceUnixEpoch::now();
844-
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(3600));
879+
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(1800));
880+
881+
// Then the session is not expired: we enforce a minimum of 1 hour
882+
assert!(!session.expired());
883+
884+
// But when more than an hour has passed
885+
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(3601));
845886

846887
// Then the session is expired
847888
assert!(session.expired());
848889
}
849890

891+
#[async_test]
892+
#[cfg(feature = "_disable-minimum-rotation-period-ms")]
893+
async fn with_disable_minrotperiod_feature_sessions_can_expire_quickly() {
894+
// Given a session with a 100ms expiration
895+
let mut session = create_session(EncryptionSettings {
896+
rotation_period: Duration::from_millis(100),
897+
..Default::default()
898+
})
899+
.await;
900+
901+
// When less than an hour has passed
902+
let now = SecondsSinceUnixEpoch::now();
903+
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(1800));
904+
905+
// Then the session is expired: the feature flag has prevented us enforcing a
906+
// minimum
907+
assert!(session.expired());
908+
}
909+
850910
#[async_test]
851911
async fn session_with_zero_msgs_rotation_is_not_expired_initially() {
852912
// Given a session that is supposed to expire after zero messages

0 commit comments

Comments
 (0)