From 344631b4ee45f6d9e531d0efda73eff80da349fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 13 Aug 2020 14:37:33 +0200 Subject: [PATCH] crypto: Respect the encryption settings of a room when creating sessions. --- matrix_sdk_base/src/client.rs | 7 ++- matrix_sdk_base/src/models/room.rs | 32 +++++++++- matrix_sdk_crypto/src/lib.rs | 4 +- matrix_sdk_crypto/src/machine.rs | 49 +++++++++++---- matrix_sdk_crypto/src/memory_stores.rs | 5 +- matrix_sdk_crypto/src/olm/account.rs | 22 +++++-- matrix_sdk_crypto/src/olm/group_sessions.rs | 68 +++++++++++++++++++-- matrix_sdk_crypto/src/olm/mod.rs | 9 ++- matrix_sdk_crypto/src/store/memorystore.rs | 5 +- 9 files changed, 171 insertions(+), 30 deletions(-) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 54cbd0fd..b9f96fe4 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -1305,7 +1305,12 @@ impl BaseClient { let joined_members = room.joined_members.keys(); let invited_members = room.joined_members.keys(); let members: Vec<&UserId> = joined_members.chain(invited_members).collect(); - Ok(o.share_group_session(room_id, members.into_iter()).await?) + Ok(o.share_group_session( + room_id, + members.into_iter(), + room.encrypted.clone().unwrap_or_default(), + ) + .await?) } None => panic!("Olm machine wasn't started"), } diff --git a/matrix_sdk_base/src/models/room.rs b/matrix_sdk_base/src/models/room.rs index d3476c2f..d7ee36ce 100644 --- a/matrix_sdk_base/src/models/room.rs +++ b/matrix_sdk_base/src/models/room.rs @@ -13,13 +13,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(feature = "messages")] -use std::ops::DerefMut; use std::{ collections::{BTreeMap, HashMap, HashSet}, convert::TryFrom, }; +#[cfg(feature = "messages")] +use std::ops::DerefMut; + +#[cfg(feature = "encryption")] +use std::time::Duration; + +#[cfg(feature = "encryption")] +use matrix_sdk_crypto::EncryptionSettings; + #[cfg(feature = "messages")] use matrix_sdk_common::events::{ room::redaction::SyncRedactionEvent, AnyPossiblyRedactedSyncMessageEvent, AnySyncMessageEvent, @@ -111,6 +118,16 @@ pub struct EncryptionInfo { rotation_period_messages: u64, } +impl Default for EncryptionInfo { + fn default() -> Self { + Self { + algorithm: Algorithm::MegolmV1AesSha2, + rotation_period_ms: 604_800_000, + rotation_period_messages: 100, + } + } +} + impl EncryptionInfo { /// The encryption algorithm that should be used to encrypt messages in the /// room. @@ -143,6 +160,17 @@ impl From<&SyncStateEvent> for EncryptionInfo { } } +#[cfg(feature = "encryption")] +impl Into for EncryptionInfo { + fn into(self) -> EncryptionSettings { + EncryptionSettings { + algorithm: self.algorithm, + rotation_period: Duration::from_millis(self.rotation_period_messages), + rotation_period_msgs: self.rotation_period_messages, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Tombstone { /// A server-defined message. diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 36b842d4..829a66bb 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -39,7 +39,9 @@ pub use device::{Device, TrustState}; pub use error::{MegolmError, OlmError}; pub use machine::{OlmMachine, OneTimeKeys}; pub use memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices}; -pub use olm::{Account, IdentityKeys, InboundGroupSession, OutboundGroupSession, Session}; +pub use olm::{ + Account, EncryptionSettings, IdentityKeys, InboundGroupSession, OutboundGroupSession, Session, +}; #[cfg(feature = "sqlite_cryptostore")] pub use store::sqlite::SqliteStore; pub use store::{CryptoStore, CryptoStoreError}; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 127be8fe..028463ad 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -53,8 +53,8 @@ use super::{ device::Device, error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult}, olm::{ - Account, GroupSessionKey, IdentityKeys, InboundGroupSession, OlmMessage, - OutboundGroupSession, + Account, EncryptionSettings, GroupSessionKey, IdentityKeys, InboundGroupSession, + OlmMessage, OutboundGroupSession, }, store::{memorystore::MemoryStore, Result as StoreResult}, verification::{Sas, VerificationMachine}, @@ -824,8 +824,16 @@ impl OlmMachine { /// /// This also creates a matching inbound group session and saves that one in /// the store. - async fn create_outbound_group_session(&self, room_id: &RoomId) -> OlmResult<()> { - let (outbound, inbound) = self.account.create_group_session_pair(room_id).await; + async fn create_outbound_group_session( + &self, + room_id: &RoomId, + settings: EncryptionSettings, + ) -> OlmResult<()> { + let (outbound, inbound) = self + .account + .create_group_session_pair(room_id, settings) + .await + .map_err(|_| EventError::UnsupportedAlgorithm)?; let _ = self.store.save_inbound_group_session(inbound).await?; @@ -835,6 +843,15 @@ impl OlmMachine { Ok(()) } + #[cfg(test)] + async fn create_outnbound_group_session_with_defaults( + &self, + room_id: &RoomId, + ) -> OlmResult<()> { + self.create_outbound_group_session(room_id, EncryptionSettings::default()) + .await + } + /// Get an outbound group session for a room, if one exists. /// /// # Arguments @@ -961,7 +978,6 @@ impl OlmMachine { self.outbound_group_sessions.remove(room_id).is_some() } - // TODO accept an algorithm here /// Get to-device requests to share a group session with users in a room. /// /// # Arguments @@ -970,15 +986,18 @@ impl OlmMachine { /// used. /// /// `users` - The list of users that should receive the group session. - pub async fn share_group_session<'a, I>( + pub async fn share_group_session<'a, I, S>( &self, room_id: &RoomId, users: I, + encryption_settings: S, ) -> OlmResult> where I: IntoIterator, + S: Into + Sized, { - self.create_outbound_group_session(room_id).await?; + self.create_outbound_group_session(room_id, encryption_settings.into()) + .await?; let session = self.outbound_group_sessions.get(room_id).unwrap(); if session.shared() { @@ -1373,7 +1392,7 @@ mod test { use crate::{ machine::{OlmMachine, OneTimeKeys}, - verify_json, Device, + verify_json, Device, EncryptionSettings, }; use matrix_sdk_common::{ @@ -1618,7 +1637,7 @@ mod test { let room_id = room_id!("!test:example.org"); machine - .create_outbound_group_session(&room_id) + .create_outnbound_group_session_with_defaults(&room_id) .await .unwrap(); assert!(machine.outbound_group_sessions.get(&room_id).is_some()); @@ -1825,7 +1844,11 @@ mod test { let room_id = room_id!("!test:example.org"); let to_device_requests = alice - .share_group_session(&room_id, [bob.user_id.clone()].iter()) + .share_group_session( + &room_id, + [bob.user_id.clone()].iter(), + EncryptionSettings::default(), + ) .await .unwrap(); @@ -1868,7 +1891,11 @@ mod test { let room_id = room_id!("!test:example.org"); let to_device_requests = alice - .share_group_session(&room_id, [bob.user_id().clone()].iter()) + .share_group_session( + &room_id, + [bob.user_id().clone()].iter(), + EncryptionSettings::default(), + ) .await .unwrap(); diff --git a/matrix_sdk_crypto/src/memory_stores.rs b/matrix_sdk_crypto/src/memory_stores.rs index 1e1f94fa..2bceb1dc 100644 --- a/matrix_sdk_crypto/src/memory_stores.rs +++ b/matrix_sdk_crypto/src/memory_stores.rs @@ -258,7 +258,10 @@ mod test { let (account, _) = get_account_and_session().await; let room_id = room_id!("!test:localhost"); - let (outbound, _) = account.create_group_session_pair(&room_id).await; + let (outbound, _) = account + .create_group_session_pair(&room_id, Default::default()) + .await + .unwrap(); assert_eq!(0, outbound.message_index().await); assert!(!outbound.shared()); diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 5ee89216..68bf4690 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -42,7 +42,7 @@ pub use olm_rs::{ utility::OlmUtility, }; -use super::{InboundGroupSession, OutboundGroupSession, Session}; +use super::{EncryptionSettings, InboundGroupSession, OutboundGroupSession, Session}; use crate::{device::Device, error::SessionCreationError}; /// Account holding identity keys for which sessions can be created. @@ -537,12 +537,24 @@ impl Account { /// # Arguments /// /// * `room_id` - The ID of the room where the group session will be used. + /// + /// * `settings` - Settings determining the algorithm and rotation period of + /// the outbound group session. pub(crate) async fn create_group_session_pair( &self, room_id: &RoomId, - ) -> (OutboundGroupSession, InboundGroupSession) { - let outbound = - OutboundGroupSession::new(self.device_id.clone(), self.identity_keys.clone(), room_id); + settings: EncryptionSettings, + ) -> Result<(OutboundGroupSession, InboundGroupSession), ()> { + if settings.algorithm != Algorithm::MegolmV1AesSha2 { + return Err(()); + } + + let outbound = OutboundGroupSession::new( + self.device_id.clone(), + self.identity_keys.clone(), + room_id, + settings, + ); let identity_keys = self.identity_keys(); let sender_key = identity_keys.curve25519(); @@ -556,7 +568,7 @@ impl Account { ) .expect("Can't create inbound group session from a newly created outbound group session"); - (outbound, inbound) + Ok((outbound, inbound)) } } diff --git a/matrix_sdk_crypto/src/olm/group_sessions.rs b/matrix_sdk_crypto/src/olm/group_sessions.rs index 717877b6..ba349fbd 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions.rs @@ -16,14 +16,18 @@ use std::{ convert::TryInto, fmt, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, + time::Duration, }; use matrix_sdk_common::{ events::{ - room::{encrypted::EncryptedEventContent, message::MessageEventContent}, + room::{ + encrypted::EncryptedEventContent, encryption::EncryptionEventContent, + message::MessageEventContent, + }, Algorithm, AnySyncRoomEvent, EventType, SyncMessageEvent, }, identifiers::{DeviceId, RoomId}, @@ -47,6 +51,49 @@ pub use olm_rs::{ use crate::error::{EventError, MegolmResult}; +const ROTATION_PERIOD: Duration = Duration::from_millis(604800000); +const ROTATION_MESSAGES: u64 = 100; + +/// Settings for an encrypted room. +/// +/// This determines the algorithm and rotation periods of a group session. +#[derive(Debug)] +pub struct EncryptionSettings { + /// The encryption algorithm that should be used in the room. + pub algorithm: Algorithm, + /// How long the session should be used before changing it. + pub rotation_period: Duration, + /// How many messages should be sent before changing the session. + pub rotation_period_msgs: u64, +} + +impl Default for EncryptionSettings { + fn default() -> Self { + Self { + algorithm: Algorithm::MegolmV1AesSha2, + rotation_period: ROTATION_PERIOD, + rotation_period_msgs: ROTATION_MESSAGES, + } + } +} + +impl From<&EncryptionEventContent> for EncryptionSettings { + fn from(content: &EncryptionEventContent) -> Self { + let rotation_period: Duration = content + .rotation_period_ms + .map_or(ROTATION_PERIOD, |r| Duration::from_millis(r.into())); + let rotation_period_msgs: u64 = content + .rotation_period_msgs + .map_or(ROTATION_MESSAGES, Into::into); + + Self { + algorithm: content.algorithm.clone(), + rotation_period, + rotation_period_msgs, + } + } +} + /// The private session key of a group session. /// Can be used to create a new inbound group session. #[derive(Clone, Debug, Serialize, Zeroize)] @@ -250,8 +297,9 @@ pub struct OutboundGroupSession { session_id: Arc, room_id: Arc, creation_time: Arc, - message_count: Arc, + message_count: Arc, shared: Arc, + settings: Arc, } impl OutboundGroupSession { @@ -267,10 +315,14 @@ impl OutboundGroupSession { /// session. /// /// * `room_id` - The id of the room that the session is used in. + /// + /// * `settings` - Settings determining the algorithm and rotation period of + /// the outbound group session. pub fn new( device_id: Arc>, identity_keys: Arc, room_id: &RoomId, + settings: EncryptionSettings, ) -> Self { let session = OlmOutboundGroupSession::new(); let session_id = session.session_id(); @@ -282,8 +334,9 @@ impl OutboundGroupSession { account_identity_keys: identity_keys, session_id: Arc::new(session_id), creation_time: Arc::new(Instant::now()), - message_count: Arc::new(AtomicUsize::new(0)), + message_count: Arc::new(AtomicU64::new(0)), shared: Arc::new(AtomicBool::new(false)), + settings: Arc::new(settings), } } @@ -296,6 +349,7 @@ impl OutboundGroupSession { /// * `plaintext` - The plaintext that should be encrypted. pub(crate) async fn encrypt_helper(&self, plaintext: String) -> String { let session = self.inner.lock().await; + self.message_count.fetch_add(1, Ordering::SeqCst); session.encrypt(plaintext) } @@ -349,8 +403,10 @@ impl OutboundGroupSession { /// A session will expire after some time or if enough messages have been /// encrypted using it. pub fn expired(&self) -> bool { - // TODO implement this. - false + let count = self.message_count.load(Ordering::SeqCst); + + count >= self.settings.rotation_period_msgs + || self.creation_time.elapsed() >= self.settings.rotation_period } /// Mark the session as shared. diff --git a/matrix_sdk_crypto/src/olm/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index d406559d..af1416a4 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -17,7 +17,9 @@ mod group_sessions; mod session; pub use account::{Account, IdentityKeys}; -pub use group_sessions::{GroupSessionKey, InboundGroupSession, OutboundGroupSession}; +pub use group_sessions::{ + EncryptionSettings, GroupSessionKey, InboundGroupSession, OutboundGroupSession, +}; pub use session::{OlmMessage, Session}; #[cfg(test)] @@ -179,7 +181,10 @@ pub(crate) mod test { let alice = Account::new(&alice_id(), &alice_device_id()); let room_id = room_id!("!test:localhost"); - let (outbound, _) = alice.create_group_session_pair(&room_id).await; + let (outbound, _) = alice + .create_group_session_pair(&room_id, Default::default()) + .await + .unwrap(); assert_eq!(0, outbound.message_index().await); assert!(!outbound.shared()); diff --git a/matrix_sdk_crypto/src/store/memorystore.rs b/matrix_sdk_crypto/src/store/memorystore.rs index d901e0b5..1c76ca73 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -165,7 +165,10 @@ mod test { let (account, _) = get_account_and_session().await; let room_id = room_id!("!test:localhost"); - let (outbound, _) = account.create_group_session_pair(&room_id).await; + let (outbound, _) = account + .create_group_session_pair(&room_id, Default::default()) + .await + .unwrap(); let inbound = InboundGroupSession::new( "test_key", "test_key",