diff --git a/crates/matrix-sdk-common/src/util.rs b/crates/matrix-sdk-common/src/util.rs index ccc84902b..7670836e6 100644 --- a/crates/matrix-sdk-common/src/util.rs +++ b/crates/matrix-sdk-common/src/util.rs @@ -1,5 +1,5 @@ use instant::SystemTime; -use ruma::MilliSecondsSinceUnixEpoch; +use ruma::{MilliSecondsSinceUnixEpoch, SecondsSinceUnixEpoch}; /// Platform agnostic helper function to create MilliSecondsSinceUnixEpoch pub fn milli_seconds_since_unix_epoch() -> MilliSecondsSinceUnixEpoch { @@ -9,3 +9,11 @@ pub fn milli_seconds_since_unix_epoch() -> MilliSecondsSinceUnixEpoch { duration.as_millis().try_into().expect("can't convert milliseconds since UNIXEPOCH"); MilliSecondsSinceUnixEpoch(millis) } + +/// Platform agnostic helper function to create SecondsSinceUnixEpoch +pub fn seconds_since_unix_epoch() -> SecondsSinceUnixEpoch { + let duration = + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("now is always higher"); + let millis = duration.as_secs().try_into().expect("can't convert seconds since UNIXEPOCH"); + SecondsSinceUnixEpoch(millis) +} diff --git a/crates/matrix-sdk-crypto/src/backups/keys/recovery.rs b/crates/matrix-sdk-crypto/src/backups/keys/recovery.rs index 3017aa34f..993bc6760 100644 --- a/crates/matrix-sdk-crypto/src/backups/keys/recovery.rs +++ b/crates/matrix-sdk-crypto/src/backups/keys/recovery.rs @@ -122,7 +122,7 @@ impl RecoveryKey { let decoded = Zeroizing::new(crate::utilities::decode(key)?); if decoded.len() != Self::KEY_SIZE { - Err(DecodeError::Length(decoded.len(), Self::KEY_SIZE)) + Err(DecodeError::Length(Self::KEY_SIZE, decoded.len())) } else { let mut key = Box::new([0u8; Self::KEY_SIZE]); key.copy_from_slice(&decoded); diff --git a/crates/matrix-sdk-crypto/src/identities/device.rs b/crates/matrix-sdk-crypto/src/identities/device.rs index af946d340..2324ea560 100644 --- a/crates/matrix-sdk-crypto/src/identities/device.rs +++ b/crates/matrix-sdk-crypto/src/identities/device.rs @@ -525,7 +525,8 @@ impl ReadOnlyDevice { }; let session = if let Some(s) = store.get_sessions(&sender_key.to_base64()).await? { - let sessions = s.lock().await; + let mut sessions = s.lock().await; + sessions.sort_by_key(|s| s.last_use_time); sessions.get(0).cloned() } else { None diff --git a/crates/matrix-sdk-crypto/src/olm/account.rs b/crates/matrix-sdk-crypto/src/olm/account.rs index 839d11c40..ee2172b27 100644 --- a/crates/matrix-sdk-crypto/src/olm/account.rs +++ b/crates/matrix-sdk-crypto/src/olm/account.rs @@ -23,7 +23,7 @@ use std::{ }, }; -use matrix_sdk_common::{instant::Instant, locks::Mutex}; +use matrix_sdk_common::{locks::Mutex, util::seconds_since_unix_epoch}; use ruma::{ api::client::keys::{ upload_keys, @@ -921,7 +921,7 @@ impl ReadOnlyAccount { ) -> Session { let session = self.inner.lock().await.create_outbound_session(identity_key, one_time_key); - let now = Instant::now(); + let now = seconds_since_unix_epoch(); let session_id = session.session_id(); Session { @@ -932,8 +932,8 @@ impl ReadOnlyAccount { session_id: session_id.into(), sender_key: identity_key, created_using_fallback_key: fallback_used, - creation_time: Arc::new(now), - last_use_time: Arc::new(now), + creation_time: now, + last_use_time: now, } } @@ -1017,7 +1017,7 @@ impl ReadOnlyAccount { let result = self.inner.lock().await.create_inbound_session(&their_identity_key, message)?; - let now = Instant::now(); + let now = seconds_since_unix_epoch(); let session_id = result.session.session_id(); let session = Session { @@ -1028,8 +1028,8 @@ impl ReadOnlyAccount { session_id: session_id.into(), sender_key: their_identity_key, created_using_fallback_key: false, - creation_time: Arc::new(now), - last_use_time: Arc::new(now), + creation_time: now, + last_use_time: now, }; Ok(InboundCreationResult { session, plaintext: result.plaintext }) diff --git a/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs b/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs index 88f240927..28d02710c 100644 --- a/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs +++ b/crates/matrix-sdk-crypto/src/olm/group_sessions/mod.rs @@ -161,11 +161,14 @@ impl TryFrom for ExportedRoomKey { #[cfg(all(test, any(target_os = "linux", target_arch = "wasm32")))] mod tests { - use std::{sync::Arc, time::Duration}; + use std::time::Duration; - use matrix_sdk_common::instant::Instant; + use matrix_sdk_common::instant::SystemTime; use matrix_sdk_test::async_test; - use ruma::{device_id, events::room::message::RoomMessageEventContent, room_id, user_id}; + use ruma::{ + device_id, events::room::message::RoomMessageEventContent, room_id, user_id, + SecondsSinceUnixEpoch, + }; use super::EncryptionSettings; use crate::{MegolmError, ReadOnlyAccount}; @@ -201,7 +204,8 @@ mod tests { assert!(!session.expired()); // FIXME: this might break on macosx and windows - session.creation_time = Arc::new(Instant::now() - Duration::from_secs(60 * 60)); + let time = SystemTime::now() - Duration::from_secs(60 * 60); + session.creation_time = SecondsSinceUnixEpoch::from_system_time(time).unwrap(); assert!(session.expired()); Ok(()) diff --git a/crates/matrix-sdk-crypto/src/olm/group_sessions/outbound.rs b/crates/matrix-sdk-crypto/src/olm/group_sessions/outbound.rs index fb0e5f3ce..78d6259df 100644 --- a/crates/matrix-sdk-crypto/src/olm/group_sessions/outbound.rs +++ b/crates/matrix-sdk-crypto/src/olm/group_sessions/outbound.rs @@ -24,7 +24,7 @@ use std::{ }; use dashmap::DashMap; -use matrix_sdk_common::{instant::Instant, locks::Mutex}; +use matrix_sdk_common::{locks::Mutex, util::seconds_since_unix_epoch}; use ruma::{ events::{ room::{ @@ -37,7 +37,7 @@ use ruma::{ room_key::ToDeviceRoomKeyEventContent, AnyToDeviceEventContent, }, - DeviceId, EventEncryptionAlgorithm, RoomId, TransactionId, UserId, + DeviceId, EventEncryptionAlgorithm, RoomId, SecondsSinceUnixEpoch, TransactionId, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -49,7 +49,6 @@ pub use vodozemac::{ PickleError, }; -use super::super::{deserialize_instant, serialize_instant}; use crate::{Device, ToDeviceRequest}; const ROTATION_PERIOD: Duration = Duration::from_millis(604800000); @@ -118,7 +117,7 @@ pub struct OutboundGroupSession { account_identity_keys: Arc, session_id: Arc, room_id: Arc, - pub(crate) creation_time: Arc, + pub(crate) creation_time: SecondsSinceUnixEpoch, message_count: Arc, shared: Arc, invalidated: Arc, @@ -175,7 +174,7 @@ impl OutboundGroupSession { device_id, account_identity_keys: identity_keys, session_id: session_id.into(), - creation_time: Arc::new(Instant::now()), + creation_time: seconds_since_unix_epoch(), message_count: Arc::new(AtomicU64::new(0)), shared: Arc::new(AtomicBool::new(false)), invalidated: Arc::new(AtomicBool::new(false)), @@ -305,6 +304,18 @@ impl OutboundGroupSession { ) } + fn elapsed(&self) -> bool { + let creation_time = Duration::from_secs(self.creation_time.get().into()); + let now = Duration::from_secs(seconds_since_unix_epoch().get().into()); + + // Since the encryption settings are provided by users and not + // checked someone could set a really low rotation period so + // clamp it to an hour. + now.checked_sub(creation_time) + .map(|elapsed| elapsed >= max(self.settings.rotation_period, Duration::from_secs(3600))) + .unwrap_or(true) + } + /// Check if the session has expired and if it should be rotated. /// /// A session will expire after some time or if enough messages have been @@ -312,12 +323,7 @@ impl OutboundGroupSession { pub fn expired(&self) -> bool { let count = self.message_count.load(Ordering::SeqCst); - count >= self.settings.rotation_period_msgs - || self.creation_time.elapsed() - // Since the encryption settings are provided by users and not - // checked someone could set a really low rotation period so - // clamp it to an hour. - >= max(self.settings.rotation_period, Duration::from_secs(3600)) + count >= self.settings.rotation_period_msgs || self.elapsed() } /// Has the session been invalidated. @@ -488,7 +494,7 @@ impl OutboundGroupSession { account_identity_keys: identity_keys, session_id: session_id.into(), room_id: pickle.room_id, - creation_time: pickle.creation_time.into(), + creation_time: pickle.creation_time, message_count: AtomicU64::from(pickle.message_count).into(), shared: AtomicBool::from(pickle.shared).into(), invalidated: AtomicBool::from(pickle.invalidated).into(), @@ -519,7 +525,7 @@ impl OutboundGroupSession { pickle, room_id: self.room_id.clone(), settings: self.settings.clone(), - creation_time: *self.creation_time, + creation_time: self.creation_time, message_count: self.message_count.load(Ordering::SeqCst), shared: self.shared(), invalidated: self.invalidated(), @@ -577,8 +583,7 @@ pub struct PickledOutboundGroupSession { /// The room id this session is used for. pub room_id: Arc, /// The timestamp when this session was created. - #[serde(deserialize_with = "deserialize_instant", serialize_with = "serialize_instant")] - pub creation_time: Instant, + pub creation_time: SecondsSinceUnixEpoch, /// The number of messages this session has already encrypted. pub message_count: u64, /// Is the session shared. diff --git a/crates/matrix-sdk-crypto/src/olm/mod.rs b/crates/matrix-sdk-crypto/src/olm/mod.rs index 4e7ae7687..65f1c7b75 100644 --- a/crates/matrix-sdk-crypto/src/olm/mod.rs +++ b/crates/matrix-sdk-crypto/src/olm/mod.rs @@ -30,33 +30,11 @@ pub use group_sessions::{ EncryptionSettings, ExportedRoomKey, InboundGroupSession, OutboundGroupSession, PickledInboundGroupSession, PickledOutboundGroupSession, SessionKey, ShareInfo, }; -use matrix_sdk_common::instant::{Duration, Instant}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub use session::{PickledSession, Session}; pub use signing::{CrossSigningStatus, PickledCrossSigningIdentity, PrivateCrossSigningIdentity}; pub(crate) use utility::VerifyJson; pub use vodozemac::olm::IdentityKeys; -pub(crate) fn serialize_instant(instant: &Instant, serializer: S) -> Result -where - S: Serializer, -{ - let duration = instant.elapsed(); - duration.serialize(serializer) -} - -pub(crate) fn deserialize_instant<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let duration = Duration::deserialize(deserializer)?; - let now = Instant::now(); - let instant = now - .checked_sub(duration) - .ok_or_else(|| serde::de::Error::custom("Can't subtract the current instant"))?; - Ok(instant) -} - #[cfg(test)] pub(crate) mod tests { use matches::assert_matches; diff --git a/crates/matrix-sdk-crypto/src/olm/session.rs b/crates/matrix-sdk-crypto/src/olm/session.rs index b728972d9..9bdfea6ba 100644 --- a/crates/matrix-sdk-crypto/src/olm/session.rs +++ b/crates/matrix-sdk-crypto/src/olm/session.rs @@ -14,7 +14,7 @@ use std::{collections::BTreeMap, fmt, sync::Arc}; -use matrix_sdk_common::{instant::Instant, locks::Mutex}; +use matrix_sdk_common::{locks::Mutex, util::seconds_since_unix_epoch}; use ruma::{ events::{ room::encrypted::{ @@ -23,7 +23,7 @@ use ruma::{ }, AnyToDeviceEventContent, EventContent, }, - DeviceId, UserId, + DeviceId, SecondsSinceUnixEpoch, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -32,7 +32,7 @@ use vodozemac::{ Curve25519PublicKey, }; -use super::{deserialize_instant, serialize_instant, IdentityKeys}; +use super::IdentityKeys; use crate::{ error::{EventError, OlmResult}, ReadOnlyDevice, @@ -57,9 +57,9 @@ pub struct Session { /// Has this been created using the fallback key pub created_using_fallback_key: bool, /// When the session was created - pub creation_time: Arc, + pub creation_time: SecondsSinceUnixEpoch, /// When the session was last used - pub last_use_time: Arc, + pub last_use_time: SecondsSinceUnixEpoch, } #[cfg(not(tarpaulin_include))] @@ -83,7 +83,7 @@ impl Session { /// * `message` - The Olm message that should be decrypted. pub async fn decrypt(&mut self, message: &OlmMessage) -> Result { let plaintext = self.inner.lock().await.decrypt(message)?; - self.last_use_time = Arc::new(Instant::now()); + self.last_use_time = seconds_since_unix_epoch(); Ok(plaintext) } @@ -101,7 +101,7 @@ impl Session { /// * `plaintext` - The plaintext that should be encrypted. pub(crate) async fn encrypt_helper(&mut self, plaintext: &str) -> OlmMessage { let message = self.inner.lock().await.encrypt(plaintext); - self.last_use_time = Arc::new(Instant::now()); + self.last_use_time = seconds_since_unix_epoch(); message } @@ -173,8 +173,8 @@ impl Session { pickle, sender_key: self.sender_key, created_using_fallback_key: self.created_using_fallback_key, - creation_time: *self.creation_time, - last_use_time: *self.last_use_time, + creation_time: self.creation_time, + last_use_time: self.last_use_time, } } @@ -212,8 +212,8 @@ impl Session { session_id: session_id.into(), created_using_fallback_key: pickle.created_using_fallback_key, sender_key: pickle.sender_key, - creation_time: Arc::new(pickle.creation_time), - last_use_time: Arc::new(pickle.last_use_time), + creation_time: pickle.creation_time, + last_use_time: pickle.last_use_time, } } } @@ -238,10 +238,8 @@ pub struct PickledSession { /// Was the session created using a fallback key. #[serde(default)] pub created_using_fallback_key: bool, - /// The relative time elapsed since the session was created. - #[serde(deserialize_with = "deserialize_instant", serialize_with = "serialize_instant")] - pub creation_time: Instant, - /// The relative time elapsed since the session was last used. - #[serde(deserialize_with = "deserialize_instant", serialize_with = "serialize_instant")] - pub last_use_time: Instant, + /// The Unix timestamp when the session was created. + pub creation_time: SecondsSinceUnixEpoch, + /// The Unix timestamp when the session was last used. + pub last_use_time: SecondsSinceUnixEpoch, } diff --git a/crates/matrix-sdk-crypto/src/session_manager/sessions.rs b/crates/matrix-sdk-crypto/src/session_manager/sessions.rs index 6994629f1..e0273c5bd 100644 --- a/crates/matrix-sdk-crypto/src/session_manager/sessions.rs +++ b/crates/matrix-sdk-crypto/src/session_manager/sessions.rs @@ -19,6 +19,7 @@ use std::{ }; use dashmap::{DashMap, DashSet}; +use matrix_sdk_common::util::seconds_since_unix_epoch; use ruma::{ api::client::keys::claim_keys::v3::{ Request as KeysClaimRequest, Response as KeysClaimResponse, @@ -83,7 +84,7 @@ impl SessionManager { if let Some(sessions) = sessions { let mut sessions = sessions.lock().await; - sessions.sort_by_key(|s| s.creation_time.clone()); + sessions.sort_by_key(|s| s.creation_time); let session = sessions.get(0); @@ -94,7 +95,15 @@ impl SessionManager { "Marking session to be unwedged" ); - if session.creation_time.elapsed() > Self::UNWEDGING_INTERVAL { + let creation_time = Duration::from_secs(session.creation_time.get().into()); + let now = Duration::from_secs(seconds_since_unix_epoch().get().into()); + + let should_unwedge = now + .checked_sub(creation_time) + .map(|elapsed| elapsed > Self::UNWEDGING_INTERVAL) + .unwrap_or(true); + + if should_unwedge { self.users_for_key_claim .entry(device.user_id().to_owned()) .or_insert_with(DashSet::new) @@ -433,15 +442,16 @@ mod tests { #[async_test] #[cfg(target_os = "linux")] async fn session_unwedging() { - use matrix_sdk_common::instant::{Duration, Instant}; - use ruma::DeviceKeyAlgorithm; + use matrix_sdk_common::instant::{Duration, SystemTime}; + use ruma::{DeviceKeyAlgorithm, SecondsSinceUnixEpoch}; let manager = session_manager().await; let bob = bob_account(); let (_, mut session) = bob.create_session_for(&manager.account).await; let bob_device = ReadOnlyDevice::from_account(&bob).await; - session.creation_time = Arc::new(Instant::now() - Duration::from_secs(3601)); + let time = SystemTime::now() - Duration::from_secs(3601); + session.creation_time = SecondsSinceUnixEpoch::from_system_time(time).unwrap(); manager.store.save_devices(&[bob_device.clone()]).await.unwrap(); manager.store.save_sessions(&[session]).await.unwrap();