Skip to content

Commit

Permalink
Merge pull request #1460 from matrix-org/andy/enhance_store
Browse files Browse the repository at this point in the history
Store encryption settings
  • Loading branch information
poljar authored Mar 14, 2023
2 parents 0f7d839 + d226738 commit 4041c87
Show file tree
Hide file tree
Showing 16 changed files with 645 additions and 62 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

144 changes: 131 additions & 13 deletions bindings/matrix-sdk-crypto-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use matrix_sdk_common::deserialized_responses::VerificationState;
use matrix_sdk_crypto::{
backups::SignatureState,
olm::{IdentityKeys, InboundGroupSession, Session},
store::{Changes, CryptoStore},
store::{Changes, CryptoStore, RoomSettings as RustRoomSettings},
types::{EventEncryptionAlgorithm as RustEventEncryptionAlgorithm, SigningKey},
EncryptionSettings as RustEncryptionSettings, LocalTrust,
};
Expand Down Expand Up @@ -75,6 +75,8 @@ pub struct MigrationData {
cross_signing: CrossSigningKeyExport,
/// The list of users that the Rust SDK should track.
tracked_users: Vec<String>,
/// Map of room settings
room_settings: HashMap<String, RoomSettings>,
}

/// Struct collecting data that is important to migrate sessions to the rust-sdk
Expand Down Expand Up @@ -296,13 +298,20 @@ async fn migrate_data(
processed_steps += 1;
listener(processed_steps, total_steps);

let mut room_settings = HashMap::new();
for (room_id, settings) in data.room_settings {
let room_id = RoomId::parse(room_id)?;
room_settings.insert(room_id, settings.into());
}

let changes = Changes {
account: Some(account),
private_identity: Some(cross_signing),
sessions,
inbound_group_sessions,
recovery_key,
backup_version: data.backup_version,
room_settings,
..Default::default()
};

Expand Down Expand Up @@ -471,6 +480,45 @@ fn collect_sessions(
Ok((sessions, inbound_group_sessions))
}

/// Migrate room settings, including room algorithm and whether to block
/// untrusted devices from legacy store to Sqlite store.
///
/// Note that this method should only be used if a client has already migrated
/// account data via [migrate](#method.migrate) method, which did not include
/// room settings. For a brand new migration, the [migrate](#method.migrate)
/// method will take care of room settings automatically, if provided.
///
/// # Arguments
///
/// * `room_settings` - Map of room settings
///
/// * `path` - The path where the Sqlite store should be created.
///
/// * `passphrase` - The passphrase that should be used to encrypt the data at
/// rest in the Sqlite store. **Warning**, if no passphrase is given, the store
/// and all its data will remain unencrypted.
pub fn migrate_room_settings(
room_settings: HashMap<String, RoomSettings>,
path: &str,
passphrase: Option<String>,
) -> anyhow::Result<()> {
let runtime = Runtime::new()?;
runtime.block_on(async move {
let store = SqliteCryptoStore::open(path, passphrase.as_deref()).await?;

let mut rust_settings = HashMap::new();
for (room_id, settings) in room_settings {
let room_id = RoomId::parse(room_id)?;
rust_settings.insert(room_id, settings.into());
}

let changes = Changes { room_settings: rust_settings, ..Default::default() };
store.save_changes(changes).await?;

Ok(())
})
}

/// Callback that will be passed over the FFI to report progress
pub trait ProgressListener {
/// The callback that should be called on the Rust side
Expand All @@ -490,6 +538,7 @@ impl<T: Fn(i32, i32)> ProgressListener for T {
}

/// An encryption algorithm to be used to encrypt messages sent to a room.
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum EventEncryptionAlgorithm {
/// Olm version 1 using Curve25519, AES-256, and SHA-256.
OlmV1Curve25519AesSha2,
Expand All @@ -500,12 +549,22 @@ pub enum EventEncryptionAlgorithm {
impl From<EventEncryptionAlgorithm> for RustEventEncryptionAlgorithm {
fn from(a: EventEncryptionAlgorithm) -> Self {
match a {
EventEncryptionAlgorithm::OlmV1Curve25519AesSha2 => {
RustEventEncryptionAlgorithm::OlmV1Curve25519AesSha2
}
EventEncryptionAlgorithm::MegolmV1AesSha2 => {
RustEventEncryptionAlgorithm::MegolmV1AesSha2
EventEncryptionAlgorithm::OlmV1Curve25519AesSha2 => Self::OlmV1Curve25519AesSha2,
EventEncryptionAlgorithm::MegolmV1AesSha2 => Self::MegolmV1AesSha2,
}
}
}

impl TryFrom<RustEventEncryptionAlgorithm> for EventEncryptionAlgorithm {
type Error = serde_json::Error;

fn try_from(value: RustEventEncryptionAlgorithm) -> Result<Self, Self::Error> {
match value {
RustEventEncryptionAlgorithm::OlmV1Curve25519AesSha2 => {
Ok(Self::OlmV1Curve25519AesSha2)
}
RustEventEncryptionAlgorithm::MegolmV1AesSha2 => Ok(Self::MegolmV1AesSha2),
_ => Err(serde::de::Error::custom(format!("Unsupported algorithm {value}"))),
}
}
}
Expand Down Expand Up @@ -540,10 +599,10 @@ pub enum HistoryVisibility {
impl From<HistoryVisibility> for RustHistoryVisibility {
fn from(h: HistoryVisibility) -> Self {
match h {
HistoryVisibility::Invited => RustHistoryVisibility::Invited,
HistoryVisibility::Joined => RustHistoryVisibility::Joined,
HistoryVisibility::Shared => RustHistoryVisibility::Shared,
HistoryVisibility::WorldReadable => RustHistoryVisibility::Shared,
HistoryVisibility::Invited => Self::Invited,
HistoryVisibility::Joined => Self::Joined,
HistoryVisibility::Shared => Self::Shared,
HistoryVisibility::WorldReadable => Self::Shared,
}
}
}
Expand Down Expand Up @@ -709,6 +768,34 @@ impl From<matrix_sdk_crypto::CrossSigningStatus> for CrossSigningStatus {
}
}

/// Room encryption settings which are modified by state events or user options
#[derive(Debug, Deserialize, Serialize, PartialEq)]
pub struct RoomSettings {
/// The encryption algorithm that should be used in the room.
pub algorithm: EventEncryptionAlgorithm,
/// Should untrusted devices receive the room key, or should they be
/// excluded from the conversation.
pub only_allow_trusted_devices: bool,
}

impl TryFrom<RustRoomSettings> for RoomSettings {
type Error = serde_json::Error;

fn try_from(value: RustRoomSettings) -> Result<Self, Self::Error> {
let algorithm = value.algorithm.try_into()?;
Ok(Self { algorithm, only_allow_trusted_devices: value.only_allow_trusted_devices })
}
}

impl From<RoomSettings> for RustRoomSettings {
fn from(value: RoomSettings) -> Self {
Self {
algorithm: value.algorithm.into(),
only_allow_trusted_devices: value.only_allow_trusted_devices,
}
}
}

fn parse_user_id(user_id: &str) -> Result<OwnedUserId, CryptoStoreError> {
ruma::UserId::parse(user_id).map_err(|e| CryptoStoreError::InvalidUserId(user_id.to_owned(), e))
}
Expand All @@ -725,7 +812,7 @@ mod uniffi_types {
RequestVerificationResult, StartSasResult, Verification, VerificationRequest,
},
BackupKeys, CrossSigningKeyExport, CrossSigningStatus, DecryptedEvent, EncryptionSettings,
RoomKeyCounts,
EventEncryptionAlgorithm, RoomKeyCounts, RoomSettings,
};
}

Expand All @@ -736,7 +823,7 @@ mod test {
use tempfile::tempdir;

use super::MigrationData;
use crate::{migrate, OlmMachine};
use crate::{migrate, EventEncryptionAlgorithm, OlmMachine, RoomSettings};

#[test]
fn android_migration() -> Result<()> {
Expand Down Expand Up @@ -819,7 +906,17 @@ mod test {
"@this-is-me:matrix.org",
"@Amandine:matrix.org",
"@ganfra:matrix.org"
]
],
"room_settings": {
"!AZkqtjvtwPAuyNOXEt:matrix.org": {
"algorithm": "OlmV1Curve25519AesSha2",
"only_allow_trusted_devices": true
},
"!CWLUCoEWXSFyTCOtfL:matrix.org": {
"algorithm": "MegolmV1AesSha2",
"only_allow_trusted_devices": false
},
}
});

let migration_data: MigrationData = serde_json::from_value(data)?;
Expand Down Expand Up @@ -848,6 +945,27 @@ mod test {
let backup_keys = machine.get_backup_keys()?;
assert!(backup_keys.is_some());

let settings1 = machine.get_room_settings("!AZkqtjvtwPAuyNOXEt:matrix.org".into())?;
assert_eq!(
Some(RoomSettings {
algorithm: EventEncryptionAlgorithm::OlmV1Curve25519AesSha2,
only_allow_trusted_devices: true
}),
settings1
);

let settings2 = machine.get_room_settings("!CWLUCoEWXSFyTCOtfL:matrix.org".into())?;
assert_eq!(
Some(RoomSettings {
algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2,
only_allow_trusted_devices: false
}),
settings2
);

let settings3 = machine.get_room_settings("!XYZ:matrix.org".into())?;
assert!(settings3.is_none());

Ok(())
}
}
10 changes: 7 additions & 3 deletions bindings/matrix-sdk-crypto-ffi/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ pub struct LoggerWrapper {
pub fn set_logger(logger: Box<dyn Logger>) {
let logger = LoggerWrapper { inner: Arc::new(Mutex::new(logger)) };

let filter = EnvFilter::from_default_env().add_directive(
"matrix_sdk_crypto=trace".parse().expect("Can't parse logging filter directive"),
);
let filter = EnvFilter::from_default_env()
.add_directive(
"matrix_sdk_crypto=trace".parse().expect("Can't parse logging filter directive"),
)
.add_directive(
"matrix_sdk_sqlite=debug".parse().expect("Can't parse logging filter directive"),
);

let _ = tracing_subscriber::fmt()
.with_writer(logger)
Expand Down
103 changes: 99 additions & 4 deletions bindings/matrix-sdk-crypto-ffi/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use matrix_sdk_crypto::{
},
decrypt_room_key_export, encrypt_room_key_export,
olm::ExportedRoomKey,
store::RecoveryKey,
store::{Changes, RecoveryKey},
LocalTrust, OlmMachine as InnerMachine, UserIdentities,
};
use ruma::{
Expand Down Expand Up @@ -53,9 +53,9 @@ use crate::{
responses::{response_from_string, OwnedResponse},
BackupKeys, BackupRecoveryKey, BootstrapCrossSigningResult, CrossSigningKeyExport,
CrossSigningStatus, DecodeError, DecryptedEvent, Device, DeviceLists, EncryptionSettings,
KeyImportError, KeysImportResult, MegolmV1BackupKey, ProgressListener, Request, RequestType,
RequestVerificationResult, RoomKeyCounts, Sas, SignatureUploadRequest, StartSasResult,
UserIdentity, Verification, VerificationRequest,
EventEncryptionAlgorithm, KeyImportError, KeysImportResult, MegolmV1BackupKey,
ProgressListener, Request, RequestType, RequestVerificationResult, RoomKeyCounts, RoomSettings,
Sas, SignatureUploadRequest, StartSasResult, UserIdentity, Verification, VerificationRequest,
};

/// A high level state machine that handles E2EE for Matrix.
Expand Down Expand Up @@ -563,6 +563,101 @@ impl OlmMachine {
.map(|r| r.into()))
}

/// Get the stored room settings, such as the encryption algorithm or
/// whether to encrypt only for trusted devices.
///
/// These settings can be modified via
/// [set_room_algorithm()](#method.set_room_algorithm) and
/// [set_room_only_allow_trusted_devices()](#method.
/// set_room_only_allow_trusted_devices) methods.
pub fn get_room_settings(
&self,
room_id: String,
) -> Result<Option<RoomSettings>, CryptoStoreError> {
let room_id = RoomId::parse(room_id)?;
let settings = self
.runtime
.block_on(self.inner.store().get_room_settings(&room_id))?
.map(|v| v.try_into())
.transpose()?;
Ok(settings)
}

/// Set the room algorithm used for encrypting messages to one of the
/// available variants
pub fn set_room_algorithm(
&self,
room_id: String,
algorithm: EventEncryptionAlgorithm,
) -> Result<(), CryptoStoreError> {
let room_id = RoomId::parse(room_id)?;
self.runtime.block_on(async move {
let mut settings =
self.inner.store().get_room_settings(&room_id).await?.unwrap_or_default();
settings.algorithm = algorithm.into();
self.inner
.store()
.save_changes(Changes {
room_settings: HashMap::from([(room_id, settings)]),
..Default::default()
})
.await?;
Ok(())
})
}

/// Set flag whether this room should encrypt messages for untrusted
/// devices, or whether they should be excluded from the conversation.
///
/// Note that per-room setting may be overridden by a global
/// [set_only_allow_trusted_devices()](#method.
/// set_only_allow_trusted_devices) method.
pub fn set_room_only_allow_trusted_devices(
&self,
room_id: String,
only_allow_trusted_devices: bool,
) -> Result<(), CryptoStoreError> {
let room_id = RoomId::parse(room_id)?;
self.runtime.block_on(async move {
let mut settings =
self.inner.store().get_room_settings(&room_id).await?.unwrap_or_default();
settings.only_allow_trusted_devices = only_allow_trusted_devices;
self.inner
.store()
.save_changes(Changes {
room_settings: HashMap::from([(room_id, settings)]),
..Default::default()
})
.await?;
Ok(())
})
}

/// Check whether there is a global flag to only encrypt messages for
/// trusted devices or for everyone.
///
/// Note that if the global flag is false, individual rooms may still be
/// encrypting only for trusted devices, depending on the per-room
/// `only_allow_trusted_devices` flag.
pub fn get_only_allow_trusted_devices(&self) -> Result<bool, CryptoStoreError> {
let block = self.runtime.block_on(self.inner.store().get_only_allow_trusted_devices())?;
Ok(block)
}

/// Set global flag whether to encrypt messages for untrusted devices, or
/// whether they should be excluded from the conversation.
///
/// Note that if enabled, it will override any per-room settings.
pub fn set_only_allow_trusted_devices(
&self,
only_allow_trusted_devices: bool,
) -> Result<(), CryptoStoreError> {
self.runtime.block_on(
self.inner.store().set_only_allow_trusted_devices(only_allow_trusted_devices),
)?;
Ok(())
}

/// Share a room key with the given list of users for the given room.
///
/// After the request was sent out and a successful response was received
Expand Down
Loading

0 comments on commit 4041c87

Please sign in to comment.