crypto: Simplify and test the group session invalidation logic.

master
Damir Jelić 2020-10-08 12:40:42 +02:00
parent 23ac00c8ec
commit 19d513e3c0
2 changed files with 84 additions and 16 deletions

View File

@ -21,7 +21,7 @@ use dashmap::DashMap;
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::to_device::DeviceIdOrAllDevices, api::r0::to_device::DeviceIdOrAllDevices,
events::{room::encrypted::EncryptedEventContent, AnyMessageEventContent, EventType}, events::{room::encrypted::EncryptedEventContent, AnyMessageEventContent, EventType},
identifiers::{DeviceId, RoomId, UserId}, identifiers::{RoomId, UserId},
uuid::Uuid, uuid::Uuid,
}; };
use tracing::debug; use tracing::debug;
@ -70,15 +70,6 @@ impl GroupSessionManager {
pub fn invalidate_sessions_new_devices(&self, users: &HashSet<&UserId>) { pub fn invalidate_sessions_new_devices(&self, users: &HashSet<&UserId>) {
for session in self.outbound_group_sessions.iter() { for session in self.outbound_group_sessions.iter() {
if users.iter().any(|u| session.contains_recipient(u)) { if users.iter().any(|u| session.contains_recipient(u)) {
session.invalidate_session()
}
}
}
/// Invalidate the sessions that were sent to the given user/device pair.
pub fn invalidate_sessions(&self, user_id: &UserId, device_id: &DeviceId) {
for session in self.outbound_group_sessions.iter() {
if session.is_shared_with(user_id, device_id) {
session.invalidate_session(); session.invalidate_session();
if !session.shared() { if !session.shared() {

View File

@ -112,7 +112,7 @@ impl IdentityManager {
&self, &self,
device_keys_map: &BTreeMap<UserId, BTreeMap<DeviceIdBox, DeviceKeys>>, device_keys_map: &BTreeMap<UserId, BTreeMap<DeviceIdBox, DeviceKeys>>,
) -> StoreResult<Vec<ReadOnlyDevice>> { ) -> StoreResult<Vec<ReadOnlyDevice>> {
let mut users_with_new_devices = HashSet::new(); let mut users_with_new_or_deleted_devices = HashSet::new();
let mut changed_devices = Vec::new(); let mut changed_devices = Vec::new();
for (user_id, device_map) in device_keys_map { for (user_id, device_map) in device_keys_map {
@ -158,7 +158,7 @@ impl IdentityManager {
} }
}; };
info!("Adding a new device to the device store {:?}", device); info!("Adding a new device to the device store {:?}", device);
users_with_new_devices.insert(user_id); users_with_new_or_deleted_devices.insert(user_id);
device device
}; };
@ -173,9 +173,8 @@ impl IdentityManager {
let deleted_devices = stored_devices_set.difference(&current_devices); let deleted_devices = stored_devices_set.difference(&current_devices);
for device_id in deleted_devices { for device_id in deleted_devices {
users_with_new_or_deleted_devices.insert(user_id);
if let Some(device) = stored_devices.get(device_id) { if let Some(device) = stored_devices.get(device_id) {
self.group_manager
.invalidate_sessions(device.user_id(), device.device_id());
device.mark_as_deleted(); device.mark_as_deleted();
self.store.delete_device(device).await?; self.store.delete_device(device).await?;
} }
@ -183,7 +182,7 @@ impl IdentityManager {
} }
self.group_manager self.group_manager
.invalidate_sessions_new_devices(&users_with_new_devices); .invalidate_sessions_new_devices(&users_with_new_or_deleted_devices);
Ok(changed_devices) Ok(changed_devices)
} }
@ -369,7 +368,7 @@ pub(crate) mod test {
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::keys::get_keys::Response as KeyQueryResponse, api::r0::keys::get_keys::Response as KeyQueryResponse,
identifiers::{user_id, DeviceIdBox, UserId}, identifiers::{room_id, user_id, DeviceIdBox, RoomId, UserId},
}; };
use matrix_sdk_test::async_test; use matrix_sdk_test::async_test;
@ -397,6 +396,10 @@ pub(crate) mod test {
"WSKKLTJZCL".into() "WSKKLTJZCL".into()
} }
fn room_id() -> RoomId {
room_id!("!test:localhost")
}
fn manager() -> IdentityManager { fn manager() -> IdentityManager {
let user_id = Arc::new(user_id()); let user_id = Arc::new(user_id());
let account = ReadOnlyAccount::new(&user_id, &device_id()); let account = ReadOnlyAccount::new(&user_id, &device_id());
@ -642,4 +645,78 @@ pub(crate) mod test {
assert!(identity.is_device_signed(&device).is_ok()) assert!(identity.is_device_signed(&device).is_ok())
} }
#[async_test]
async fn test_session_invalidation() {
let manager = manager();
let room_id = room_id();
let user_id = other_user_id();
let device_id: DeviceIdBox = "SKISMLNIMH".into();
manager
.group_manager
.create_outbound_group_session(&room_id, Default::default())
.await
.unwrap();
let session = manager
.group_manager
.get_outbound_group_session(&room_id)
.unwrap();
session.add_recipient(&user_id);
session.mark_as_shared();
assert!(!session.invalidated());
assert!(!session.expired());
// Receiving a new device invalidates the session.
manager
.receive_keys_query_response(&other_key_query())
.await
.unwrap();
assert!(session.invalidated());
manager
.group_manager
.create_outbound_group_session(&room_id, Default::default())
.await
.unwrap();
let session = manager
.group_manager
.get_outbound_group_session(&room_id)
.unwrap();
session.add_recipient(&user_id);
session.mark_as_shared();
assert!(!session.invalidated());
assert!(!session.expired());
let device = manager
.store
.get_device(&user_id, &device_id)
.await
.unwrap()
.unwrap();
assert!(!device.deleted());
let response = KeyQueryResponse::try_from(response_from_file(&json!({
"device_keys": {
user_id: {}
},
"failures": {},
})))
.unwrap();
// Noticing that a device got deleted invalidates the session as well
manager
.receive_keys_query_response(&response)
.await
.unwrap();
assert!(device.deleted());
assert!(session.invalidated());
}
} }