Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: Break up the expiration tests for clarity #3198

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 145 additions & 64 deletions crates/matrix-sdk-crypto/src/olm/group_sessions/outbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,20 +735,16 @@ pub struct PickledOutboundGroupSession {

#[cfg(test)]
mod tests {
use std::{sync::atomic::Ordering, time::Duration};
use std::time::Duration;

use matrix_sdk_test::async_test;
use ruma::{
device_id,
events::room::{
encryption::RoomEncryptionEventContent, history_visibility::HistoryVisibility,
message::RoomMessageEventContent,
},
room_id, uint, user_id, EventEncryptionAlgorithm,
uint, EventEncryptionAlgorithm,
};

use super::{EncryptionSettings, ROTATION_MESSAGES, ROTATION_PERIOD};
use crate::{Account, MegolmError};

#[test]
fn test_encryption_settings_conversion() {
Expand All @@ -768,78 +764,163 @@ mod tests {
assert_eq!(settings.rotation_period_msgs, 500);
}

#[async_test]
#[cfg(any(target_os = "linux", target_os = "macos", target_arch = "wasm32"))]
async fn test_expiration() -> Result<(), MegolmError> {
use ruma::{serde::Raw, SecondsSinceUnixEpoch};

let settings = EncryptionSettings { rotation_period_msgs: 1, ..Default::default() };

let account =
Account::with_device_id(user_id!("@alice:example.org"), device_id!("DEVICEID"))
.static_data;
let (session, _) = account
.create_group_session_pair(room_id!("!test_room:example.org"), settings)
.await
.unwrap();

assert!(!session.expired());
let _ = session
.encrypt(
"m.room.message",
&Raw::new(&RoomMessageEventContent::text_plain("Test message"))?.cast(),
)
.await;
assert!(session.expired());
mod expiration {
use std::{sync::atomic::Ordering, time::Duration};

let settings = EncryptionSettings {
rotation_period: Duration::from_millis(100),
..Default::default()
use matrix_sdk_test::async_test;
use ruma::{
device_id, events::room::message::RoomMessageEventContent, room_id, serde::Raw, uint,
user_id, SecondsSinceUnixEpoch,
};

let (mut session, _) = account
.create_group_session_pair(room_id!("!test_room:example.org"), settings)
.await
.unwrap();
use crate::{olm::OutboundGroupSession, Account, EncryptionSettings, MegolmError};

#[async_test]
async fn session_is_not_expired_if_no_messages_sent_and_no_time_passed() {
// Given a session that expires after one message
let session = create_session(EncryptionSettings {
rotation_period_msgs: 1,
..Default::default()
})
.await;

assert!(!session.expired());
// When we send no messages at all

let now = SecondsSinceUnixEpoch::now();
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(3600));
assert!(session.expired());
// Then it is not expired
assert!(!session.expired());
}

let settings = EncryptionSettings { rotation_period_msgs: 0, ..Default::default() };
#[async_test]
async fn session_is_expired_if_we_rotate_every_message_and_one_was_sent(
) -> Result<(), MegolmError> {
// Given a session that expires after one message
let session = create_session(EncryptionSettings {
rotation_period_msgs: 1,
..Default::default()
})
.await;

let (session, _) = account
.create_group_session_pair(room_id!("!test_room:example.org"), settings)
.await
.unwrap();
// When we send a message
let _ = session
.encrypt(
"m.room.message",
&Raw::new(&RoomMessageEventContent::text_plain("Test message"))?.cast(),
)
.await;

assert!(!session.expired());
// Then the session is expired
assert!(session.expired());

let _ = session
.encrypt(
"m.room.message",
&Raw::new(&RoomMessageEventContent::text_plain("Test message"))?.cast(),
)
Ok(())
}

#[async_test]
async fn session_with_short_rotation_period_is_not_expired_after_no_time() {
// Given a session with a 100ms expiration
let session = create_session(EncryptionSettings {
rotation_period: Duration::from_millis(100),
..Default::default()
})
.await;
assert!(session.expired());

let settings = EncryptionSettings { rotation_period_msgs: 100_000, ..Default::default() };
// When we don't allow any time to pass

// Then it is not expired
assert!(!session.expired());
}

#[async_test]
async fn session_is_expired_after_rotation_period() {
// Given a session with a 100ms expiration
let mut session = create_session(EncryptionSettings {
rotation_period: Duration::from_millis(100),
..Default::default()
})
.await;

let (session, _) = account
.create_group_session_pair(room_id!("!test_room:example.org"), settings)
.await
.unwrap();
// When one hour has passed
let now = SecondsSinceUnixEpoch::now();
session.creation_time = SecondsSinceUnixEpoch(now.get() - uint!(3600));

assert!(!session.expired());
session.message_count.store(1000, Ordering::SeqCst);
assert!(!session.expired());
session.message_count.store(9999, Ordering::SeqCst);
assert!(!session.expired());
session.message_count.store(10_000, Ordering::SeqCst);
assert!(session.expired());
// Then the session is expired
assert!(session.expired());
}

Ok(())
#[async_test]
async fn session_with_zero_msgs_rotation_is_not_expired_initially() {
// Given a session that is supposed to expire after zero messages
let session = create_session(EncryptionSettings {
rotation_period_msgs: 0,
..Default::default()
})
.await;

// When we send no messages

// Then the session is not expired: we are protected against this nonsensical
// setup
assert!(!session.expired());
}

#[async_test]
async fn session_with_zero_msgs_rotation_expires_after_one_message(
) -> Result<(), MegolmError> {
// Given a session that is supposed to expire after zero messages
let session = create_session(EncryptionSettings {
rotation_period_msgs: 0,
..Default::default()
})
.await;

// When we send a message
let _ = session
.encrypt(
"m.room.message",
&Raw::new(&RoomMessageEventContent::text_plain("Test message"))?.cast(),
)
.await;

// Then the session is expired: we treated rotation_period_msgs=0 as if it were
// =1
assert!(session.expired());

Ok(())
}

#[async_test]
async fn session_expires_after_10k_messages_even_if_we_ask_for_more() {
// Given we asked to expire after 100K messages
let session = create_session(EncryptionSettings {
rotation_period_msgs: 100_000,
..Default::default()
})
.await;

// Sanity: it does not expire after <10K messages
assert!(!session.expired());
session.message_count.store(1000, Ordering::SeqCst);
assert!(!session.expired());
session.message_count.store(9999, Ordering::SeqCst);
assert!(!session.expired());

// When we have sent >= 10K messages
session.message_count.store(10_000, Ordering::SeqCst);

// Then it is considered expired: we enforce a maximum of 10K messages before
// rotation.
assert!(session.expired());
}

async fn create_session(settings: EncryptionSettings) -> OutboundGroupSession {
let account =
Account::with_device_id(user_id!("@alice:example.org"), device_id!("DEVICEID"))
.static_data;
let (session, _) = account
.create_group_session_pair(room_id!("!test_room:example.org"), settings)
.await
.unwrap();
session
}
}
}
Loading