crypto: Respect the encryption settings of a room when creating sessions.

This commit is contained in:
Damir Jelić 2020-08-13 14:37:33 +02:00
parent f3e03c66a5
commit 344631b4ee
9 changed files with 171 additions and 30 deletions

View file

@ -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"),
}

View file

@ -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<EncryptionEventContent>> for EncryptionInfo {
}
}
#[cfg(feature = "encryption")]
impl Into<EncryptionSettings> 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.

View file

@ -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};

View file

@ -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<Vec<OwnedToDeviceRequest>>
where
I: IntoIterator<Item = &'a UserId>,
S: Into<EncryptionSettings> + 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();

View file

@ -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());

View file

@ -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))
}
}

View file

@ -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<String>,
room_id: Arc<RoomId>,
creation_time: Arc<Instant>,
message_count: Arc<AtomicUsize>,
message_count: Arc<AtomicU64>,
shared: Arc<AtomicBool>,
settings: Arc<EncryptionSettings>,
}
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<Box<DeviceId>>,
identity_keys: Arc<IdentityKeys>,
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.

View file

@ -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());

View file

@ -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",