crypto: Store the history visiblity with the outbound session

master
Damir Jelić 2021-02-03 16:01:58 +01:00
parent 347f79d08c
commit 9e83eaf2f5
6 changed files with 69 additions and 21 deletions

View File

@ -54,8 +54,8 @@ use matrix_sdk_common::{
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_crypto::{ use matrix_sdk_crypto::{
store::{CryptoStore, CryptoStoreError}, store::{CryptoStore, CryptoStoreError},
Device, EncryptionSettings, IncomingResponse, OlmError, OlmMachine, OutgoingRequest, Sas, Device, EncryptionSettings, IncomingResponse, MegolmError, OlmError, OlmMachine,
ToDeviceRequest, UserDevices, OutgoingRequest, Sas, ToDeviceRequest, UserDevices,
}; };
use tracing::{info, warn}; use tracing::{info, warn};
use zeroize::Zeroizing; use zeroize::Zeroizing;
@ -1157,10 +1157,11 @@ impl BaseClient {
match &*olm { match &*olm {
Some(o) => { Some(o) => {
let history_visiblity = self let (history_visiblity, settings) = self
.get_room(room_id) .get_room(room_id)
.map(|r| r.history_visiblity()) .map(|r| (r.history_visiblity(), r.encryption_settings()))
.unwrap_or(HistoryVisibility::Joined); .unwrap_or((HistoryVisibility::Joined, None));
let joined = self.store.get_joined_user_ids(room_id).await?; let joined = self.store.get_joined_user_ids(room_id).await?;
let invited = self.store.get_invited_user_ids(room_id).await?; let invited = self.store.get_invited_user_ids(room_id).await?;
@ -1172,10 +1173,10 @@ impl BaseClient {
joined.iter().chain(&invited) joined.iter().chain(&invited)
}; };
Ok( let settings = settings.ok_or(MegolmError::EncryptionNotEnabled)?;
o.share_group_session(room_id, members, EncryptionSettings::default()) let settings = EncryptionSettings::new(settings, history_visiblity);
.await?,
) Ok(o.share_group_session(room_id, members, settings).await?)
} }
None => panic!("Olm machine wasn't started"), None => panic!("Olm machine wasn't started"),
} }

View File

@ -89,6 +89,16 @@ impl RoomState {
} }
} }
/// Get the `m.room.encryption` content that enabled end to end encryption
/// in the room.
pub fn encryption_settings(&self) -> Option<EncryptionEventContent> {
match self {
RoomState::Joined(r) => r.inner.encryption_settings(),
RoomState::Left(r) => r.inner.encryption_settings(),
RoomState::Invited(r) => r.inner.encryption_settings(),
}
}
/// Are the members for this room synced. /// Are the members for this room synced.
pub fn are_members_synced(&self) -> bool { pub fn are_members_synced(&self) -> bool {
if let RoomState::Joined(r) = self { if let RoomState::Joined(r) = self {

View File

@ -15,7 +15,10 @@
use std::sync::{Arc, Mutex as SyncMutex}; use std::sync::{Arc, Mutex as SyncMutex};
use matrix_sdk_common::{ use matrix_sdk_common::{
events::{room::history_visibility::HistoryVisibility, AnyStrippedStateEvent}, events::{
room::{encryption::EncryptionEventContent, history_visibility::HistoryVisibility},
AnyStrippedStateEvent,
},
identifiers::{RoomId, UserId}, identifiers::{RoomId, UserId},
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -96,6 +99,12 @@ impl StrippedRoom {
self.inner.lock().unwrap().base_info.encryption.is_some() self.inner.lock().unwrap().base_info.encryption.is_some()
} }
/// Get the `m.room.encryption` content that enabled end to end encryption
/// in the room.
pub fn encryption_settings(&self) -> Option<EncryptionEventContent> {
self.inner.lock().unwrap().base_info.encryption.clone()
}
/// Get the history visiblity policy of this room. /// Get the history visiblity policy of this room.
pub fn history_visibility(&self) -> HistoryVisibility { pub fn history_visibility(&self) -> HistoryVisibility {
self.inner self.inner

View File

@ -86,6 +86,10 @@ pub enum MegolmError {
#[error("can't finish Olm group session operation {0}")] #[error("can't finish Olm group session operation {0}")]
OlmGroupSession(#[from] OlmGroupSessionError), OlmGroupSession(#[from] OlmGroupSessionError),
/// The room where a group session should be shared is not encrypted.
#[error("The room where a group session should be shared is not encrypted")]
EncryptionNotEnabled,
/// The storage layer returned an error. /// The storage layer returned an error.
#[error(transparent)] #[error(transparent)]
Store(#[from] CryptoStoreError), Store(#[from] CryptoStoreError),

View File

@ -17,6 +17,7 @@ use matrix_sdk_common::{
api::r0::to_device::DeviceIdOrAllDevices, api::r0::to_device::DeviceIdOrAllDevices,
events::room::{ events::room::{
encrypted::{MegolmV1AesSha2Content, MegolmV1AesSha2ContentInit}, encrypted::{MegolmV1AesSha2Content, MegolmV1AesSha2ContentInit},
history_visibility::HistoryVisibility,
message::Relation, message::Relation,
}, },
uuid::Uuid, uuid::Uuid,
@ -80,6 +81,8 @@ pub struct EncryptionSettings {
pub rotation_period: Duration, pub rotation_period: Duration,
/// How many messages should be sent before changing the session. /// How many messages should be sent before changing the session.
pub rotation_period_msgs: u64, pub rotation_period_msgs: u64,
/// The history visibilty of the room when the session was created.
pub history_visibility: HistoryVisibility,
} }
impl Default for EncryptionSettings { impl Default for EncryptionSettings {
@ -88,12 +91,15 @@ impl Default for EncryptionSettings {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2, algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
rotation_period: ROTATION_PERIOD, rotation_period: ROTATION_PERIOD,
rotation_period_msgs: ROTATION_MESSAGES, rotation_period_msgs: ROTATION_MESSAGES,
history_visibility: HistoryVisibility::Shared,
} }
} }
} }
impl From<&EncryptionEventContent> for EncryptionSettings { impl EncryptionSettings {
fn from(content: &EncryptionEventContent) -> Self { /// Create new encryption settings using an `EncryptionEventContent` and a
/// history visiblity.
pub fn new(content: EncryptionEventContent, history_visibility: HistoryVisibility) -> Self {
let rotation_period: Duration = content let rotation_period: Duration = content
.rotation_period_ms .rotation_period_ms
.map_or(ROTATION_PERIOD, |r| Duration::from_millis(r.into())); .map_or(ROTATION_PERIOD, |r| Duration::from_millis(r.into()));
@ -102,12 +108,14 @@ impl From<&EncryptionEventContent> for EncryptionSettings {
.map_or(ROTATION_MESSAGES, Into::into); .map_or(ROTATION_MESSAGES, Into::into);
Self { Self {
algorithm: content.algorithm.clone(), algorithm: content.algorithm,
rotation_period, rotation_period,
rotation_period_msgs, rotation_period_msgs,
history_visibility,
} }
} }
} }
/// Outbound group session. /// Outbound group session.
/// ///
/// Outbound group sessions are used to exchange room messages between a group /// Outbound group sessions are used to exchange room messages between a group
@ -185,6 +193,11 @@ impl OutboundGroupSession {
self.invalidated.store(true, Ordering::Relaxed) self.invalidated.store(true, Ordering::Relaxed)
} }
/// Get the encryption settings of this outbound session.
pub fn settings(&self) -> &EncryptionSettings {
&self.settings
}
/// Mark the request with the given request id as sent. /// Mark the request with the given request id as sent.
/// ///
/// This removes the request from the queue and marks the set of /// This removes the request from the queue and marks the set of
@ -560,7 +573,8 @@ mod test {
use std::time::Duration; use std::time::Duration;
use matrix_sdk_common::{ use matrix_sdk_common::{
events::room::encryption::EncryptionEventContent, identifiers::EventEncryptionAlgorithm, events::room::{encryption::EncryptionEventContent, history_visibility::HistoryVisibility},
identifiers::EventEncryptionAlgorithm,
uint, uint,
}; };
@ -569,7 +583,7 @@ mod test {
#[test] #[test]
fn encryption_settings_conversion() { fn encryption_settings_conversion() {
let mut content = EncryptionEventContent::new(EventEncryptionAlgorithm::MegolmV1AesSha2); let mut content = EncryptionEventContent::new(EventEncryptionAlgorithm::MegolmV1AesSha2);
let settings = EncryptionSettings::from(&content); let settings = EncryptionSettings::new(content.clone(), HistoryVisibility::Joined);
assert_eq!(settings.rotation_period, ROTATION_PERIOD); assert_eq!(settings.rotation_period, ROTATION_PERIOD);
assert_eq!(settings.rotation_period_msgs, ROTATION_MESSAGES); assert_eq!(settings.rotation_period_msgs, ROTATION_MESSAGES);
@ -577,7 +591,7 @@ mod test {
content.rotation_period_ms = Some(uint!(3600)); content.rotation_period_ms = Some(uint!(3600));
content.rotation_period_msgs = Some(uint!(500)); content.rotation_period_msgs = Some(uint!(500));
let settings = EncryptionSettings::from(&content); let settings = EncryptionSettings::new(content, HistoryVisibility::Shared);
assert_eq!(settings.rotation_period, Duration::from_millis(3600)); assert_eq!(settings.rotation_period, Duration::from_millis(3600));
assert_eq!(settings.rotation_period_msgs, 500); assert_eq!(settings.rotation_period_msgs, 500);

View File

@ -20,7 +20,10 @@ use std::{
use dashmap::DashMap; 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, history_visibility::HistoryVisibility},
AnyMessageEventContent, EventType,
},
identifiers::{DeviceId, DeviceIdBox, RoomId, UserId}, identifiers::{DeviceId, DeviceIdBox, RoomId, UserId},
uuid::Uuid, uuid::Uuid,
}; };
@ -218,6 +221,7 @@ impl GroupSessionManager {
pub async fn collect_session_recipients( pub async fn collect_session_recipients(
&self, &self,
users: impl Iterator<Item = &UserId>, users: impl Iterator<Item = &UserId>,
history_visibility: HistoryVisibility,
outbound: &OutboundGroupSession, outbound: &OutboundGroupSession,
) -> OlmResult<(bool, HashMap<UserId, Vec<Device>>)> { ) -> OlmResult<(bool, HashMap<UserId, Vec<Device>>)> {
let users: HashSet<&UserId> = users.collect(); let users: HashSet<&UserId> = users.collect();
@ -238,6 +242,8 @@ impl GroupSessionManager {
.collect::<HashSet<_>>() .collect::<HashSet<_>>()
.is_empty(); .is_empty();
let visiblity_changed = outbound.settings().history_visibility != history_visibility;
let mut device_got_deleted_or_blacklisted = false; let mut device_got_deleted_or_blacklisted = false;
for user_id in users { for user_id in users {
@ -245,7 +251,7 @@ impl GroupSessionManager {
// If no device got deleted or blacklisted until now and no user // If no device got deleted or blacklisted until now and no user
// left check if one got deleted or blacklisted for this user. // left check if one got deleted or blacklisted for this user.
if !device_got_deleted_or_blacklisted && !user_left { if !(device_got_deleted_or_blacklisted || user_left || visiblity_changed) {
// Devices that should receive this session // Devices that should receive this session
let device_ids: HashSet<&DeviceId> = user_devices let device_ids: HashSet<&DeviceId> = user_devices
.keys() .keys()
@ -287,8 +293,9 @@ impl GroupSessionManager {
// To protect the room history we need to rotate the session if a user // To protect the room history we need to rotate the session if a user
// left or if a device got deleted/blacklisted, put differently if // left or if a device got deleted/blacklisted, put differently if
// someone leaves or gets removed from the encrypted group. // someone leaves or gets removed from the encrypted group or if the
let should_rotate = user_left || device_got_deleted_or_blacklisted; // history visiblity changed.
let should_rotate = user_left || device_got_deleted_or_blacklisted || visiblity_changed;
Ok((should_rotate, devices)) Ok((should_rotate, devices))
} }
@ -308,6 +315,7 @@ impl GroupSessionManager {
encryption_settings: impl Into<EncryptionSettings>, encryption_settings: impl Into<EncryptionSettings>,
) -> OlmResult<Vec<Arc<ToDeviceRequest>>> { ) -> OlmResult<Vec<Arc<ToDeviceRequest>>> {
let encryption_settings = encryption_settings.into(); let encryption_settings = encryption_settings.into();
let history_visibility = encryption_settings.history_visibility.clone();
let mut changes = Changes::default(); let mut changes = Changes::default();
let (outbound, inbound) = self let (outbound, inbound) = self
@ -319,7 +327,9 @@ impl GroupSessionManager {
changes.inbound_group_sessions.push(inbound); changes.inbound_group_sessions.push(inbound);
} }
let (should_rotate, devices) = self.collect_session_recipients(users, &outbound).await?; let (should_rotate, devices) = self
.collect_session_recipients(users, history_visibility, &outbound)
.await?;
let outbound = if should_rotate { let outbound = if should_rotate {
let (outbound, inbound) = self let (outbound, inbound) = self