Merge branch 'poljar/session-timestamp-improvements'

This commit is contained in:
Damir Jelić
2022-04-20 12:11:53 +02:00
9 changed files with 77 additions and 73 deletions
+9 -1
View File
@@ -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)
}
@@ -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);
@@ -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
+7 -7
View File
@@ -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 })
@@ -161,11 +161,14 @@ impl TryFrom<ToDeviceForwardedRoomKeyEventContent> 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(())
@@ -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<IdentityKeys>,
session_id: Arc<str>,
room_id: Arc<RoomId>,
pub(crate) creation_time: Arc<Instant>,
pub(crate) creation_time: SecondsSinceUnixEpoch,
message_count: Arc<AtomicU64>,
shared: Arc<AtomicBool>,
invalidated: Arc<AtomicBool>,
@@ -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<RoomId>,
/// 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.
-22
View File
@@ -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<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let duration = instant.elapsed();
duration.serialize(serializer)
}
pub(crate) fn deserialize_instant<'de, D>(deserializer: D) -> Result<Instant, D::Error>
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;
+15 -17
View File
@@ -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<Instant>,
pub creation_time: SecondsSinceUnixEpoch,
/// When the session was last used
pub last_use_time: Arc<Instant>,
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<String, DecryptionError> {
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,
}
@@ -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();