diff --git a/matrix_sdk/examples/wasm_command_bot/src/lib.rs b/matrix_sdk/examples/wasm_command_bot/src/lib.rs index 7da27ed9..b51fff06 100644 --- a/matrix_sdk/examples/wasm_command_bot/src/lib.rs +++ b/matrix_sdk/examples/wasm_command_bot/src/lib.rs @@ -1,3 +1,5 @@ +#![type_length_limit = "1693004"] + use matrix_sdk::{ api::r0::sync::sync_events::Response as SyncResponse, events::{ diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 73d0051b..eb7c0ff9 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -36,7 +36,7 @@ use tracing::{error, info, instrument}; use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore}; #[cfg(feature = "encryption")] -use matrix_sdk_base::{CryptoStoreError, OutgoingRequests}; +use matrix_sdk_base::{CryptoStoreError, OutgoingRequests, ToDeviceRequest}; use matrix_sdk_common::{ api::r0::{ @@ -72,8 +72,7 @@ use matrix_sdk_common::{ api::r0::{ keys::{claim_keys, get_keys, upload_keys}, to_device::send_event_to_device::{ - IncomingRequest as OwnedToDeviceRequest, Request as ToDeviceRequest, - Response as ToDeviceResponse, + Request as RumaToDeviceRequest, Response as ToDeviceResponse, }, }, locks::Mutex, @@ -514,7 +513,8 @@ impl Client { /// /// # Arguments /// - /// * `registration` - The easiest way to create this request is using the `RegistrationBuilder`. + /// * `registration` - The easiest way to create this request is using the `register::Request` + /// itself. /// /// /// # Examples @@ -740,7 +740,8 @@ impl Client { /// /// # Arguments /// - /// * `room_search` - The easiest way to create this request is using the `RoomListFilterBuilder`. + /// * `room_search` - The easiest way to create this request is using the + /// `get_public_rooms_filtered::Request` itself. /// /// # Examples /// ```no_run @@ -777,7 +778,8 @@ impl Client { /// /// # Arguments /// - /// * `room` - The easiest way to create this request is using the `RoomBuilder`. + /// * `room` - The easiest way to create this request is using the + /// `create_room::Request` itself. /// /// # Examples /// ```no_run @@ -811,7 +813,7 @@ impl Client { /// # Arguments /// /// * `request` - The easiest way to create this request is using the - /// `MessagesRequestBuilder`. + /// `get_message_events::Request` itself. /// /// # Examples /// ```no_run @@ -1098,11 +1100,12 @@ impl Client { } #[cfg(feature = "encryption")] - async fn send_to_device(&self, request: &OwnedToDeviceRequest) -> Result { - let request = ToDeviceRequest { - event_type: request.event_type.clone(), - txn_id: &request.txn_id, - messages: request.messages.clone(), + async fn send_to_device(&self, request: ToDeviceRequest) -> Result { + let txn_id_string = request.txn_id_string(); + let request = RumaToDeviceRequest { + event_type: request.event_type, + txn_id: &txn_id_string, + messages: request.messages, }; self.send(request).await @@ -1227,14 +1230,13 @@ impl Client { warn!("Error while querying device keys {:?}", e); } } - OutgoingRequests::KeysUpload(request) => { if let Err(e) = self.keys_upload(&r.request_id(), request).await { warn!("Error while querying device keys {:?}", e); } } OutgoingRequests::ToDeviceRequest(request) => { - if let Ok(resp) = self.send_to_device(request).await { + if let Ok(resp) = self.send_to_device(request.clone()).await { self.base_client .mark_request_as_sent(&r.request_id(), &resp) .await @@ -1317,7 +1319,7 @@ impl Client { .expect("Keys don't need to be uploaded"); for request in requests.drain(..) { - self.send_to_device(&request).await?; + self.send_to_device(request).await?; } Ok(()) diff --git a/matrix_sdk/src/device.rs b/matrix_sdk/src/device.rs index 49dfd5ab..7173e266 100644 --- a/matrix_sdk/src/device.rs +++ b/matrix_sdk/src/device.rs @@ -64,7 +64,7 @@ impl Device { let (sas, request) = self.inner.start_verification().await?; let request = ToDeviceRequest { event_type: request.event_type, - txn_id: &request.txn_id, + txn_id: &request.txn_id.to_string(), messages: request.messages, }; diff --git a/matrix_sdk/src/sas.rs b/matrix_sdk/src/sas.rs index cadaddd1..2a8e8708 100644 --- a/matrix_sdk/src/sas.rs +++ b/matrix_sdk/src/sas.rs @@ -30,7 +30,7 @@ impl Sas { if let Some(req) = self.inner.accept() { let request = ToDeviceRequest { event_type: req.event_type, - txn_id: &req.txn_id, + txn_id: &req.txn_id.to_string(), messages: req.messages, }; @@ -44,7 +44,7 @@ impl Sas { if let Some(req) = self.inner.confirm().await? { let request = ToDeviceRequest { event_type: req.event_type, - txn_id: &req.txn_id, + txn_id: &req.txn_id.to_string(), messages: req.messages, }; @@ -56,10 +56,10 @@ impl Sas { /// Cancel the interactive verification flow. pub async fn cancel(&self) -> Result<()> { - if let Some((_, req)) = self.inner.cancel() { + if let Some(req) = self.inner.cancel() { let request = ToDeviceRequest { event_type: req.event_type, - txn_id: &req.txn_id, + txn_id: &req.txn_id.to_string(), messages: req.messages, }; diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 5f2e14ea..9ecf24ca 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -39,15 +39,15 @@ use matrix_sdk_common::{ #[cfg(feature = "encryption")] use matrix_sdk_common::{ api::r0::keys::claim_keys::Request as KeysClaimRequest, - api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest, events::{room::encrypted::EncryptedEventContent, AnyMessageEventContent}, identifiers::DeviceId, uuid::Uuid, }; #[cfg(feature = "encryption")] use matrix_sdk_crypto::{ - CryptoStore, CryptoStoreError, Device, IncomingResponse, OlmError, OlmMachine, OutgoingRequest, - Sas, UserDevices, + store::{CryptoStore, CryptoStoreError}, + Device, IncomingResponse, OlmError, OlmMachine, OutgoingRequest, Sas, ToDeviceRequest, + UserDevices, }; use zeroize::Zeroizing; @@ -1304,7 +1304,7 @@ impl BaseClient { /// Get a to-device request that will share a group session for a room. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] - pub async fn share_group_session(&self, room_id: &RoomId) -> Result> { + pub async fn share_group_session(&self, room_id: &RoomId) -> Result> { let room = self.get_joined_room(room_id).await.expect("No room found"); let olm = self.olm.lock().await; diff --git a/matrix_sdk_base/src/lib.rs b/matrix_sdk_base/src/lib.rs index f96d1728..9ba33445 100644 --- a/matrix_sdk_base/src/lib.rs +++ b/matrix_sdk_base/src/lib.rs @@ -57,8 +57,8 @@ pub use state::{AllRooms, ClientState}; #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub use matrix_sdk_crypto::{ - CryptoStoreError, Device, IncomingResponse, LocalTrust, OutgoingRequest, OutgoingRequests, - ReadOnlyDevice, Sas, UserDevices, + store::CryptoStoreError, Device, IncomingResponse, LocalTrust, OutgoingRequest, + OutgoingRequests, ReadOnlyDevice, Sas, ToDeviceRequest, UserDevices, }; #[cfg(feature = "messages")] diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index f22776ad..6860d9df 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -18,7 +18,7 @@ js_int = "0.1.9" [dependencies.ruma] version = "0.0.1" git = "https://github.com/ruma/ruma" -rev = "e4cd59e7e5f8ce4c8b90948155c3d031e45f9c54" +rev = "409fbcc9d745fb7290327cb7f5defc714229ab30" features = ["client-api", "unstable-pre-spec"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index 08b544b2..ae416460 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -20,13 +20,13 @@ sqlite_cryptostore = ["sqlx"] docs = ["sqlite_cryptostore"] [dependencies] -async-trait = "0.1.38" +async-trait = "0.1.40" matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" } matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" } olm-rs = { git = 'https://gitlab.gnome.org/jhaye/olm-rs/', features = ["serde"]} -serde = { version = "1.0.115", features = ["derive"] } +serde = { version = "1.0.115", features = ["derive", "rc"] } serde_json = "1.0.57" cjson = "0.1.1" zeroize = { version = "1.1.0", features = ["zeroize_derive"] } diff --git a/matrix_sdk_crypto/src/error.rs b/matrix_sdk_crypto/src/error.rs index cc679c0c..ee4e5f1f 100644 --- a/matrix_sdk_crypto/src/error.rs +++ b/matrix_sdk_crypto/src/error.rs @@ -115,6 +115,16 @@ pub enum EventError { MissmatchedKeys, } +#[derive(Error, Debug)] +pub enum SessionUnpicklingError { + /// The underlying Olm session operation returned an error. + #[error("can't finish Olm Session operation {0}")] + OlmSession(#[from] OlmSessionError), + /// The Session timestamp was invalid. + #[error("can't load session timestamps")] + SessionTimestampError, +} + #[derive(Error, Debug)] pub enum SignatureError { #[error("the signature used a unsupported algorithm")] diff --git a/matrix_sdk_crypto/src/device.rs b/matrix_sdk_crypto/src/identities/device.rs similarity index 97% rename from matrix_sdk_crypto/src/device.rs rename to matrix_sdk_crypto/src/identities/device.rs index bfbaface..039e9c87 100644 --- a/matrix_sdk_crypto/src/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -24,25 +24,25 @@ use std::{ use atomic::Atomic; use matrix_sdk_common::{ - api::r0::{ - keys::SignedKey, to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest, - }, + api::r0::keys::SignedKey, encryption::DeviceKeys, events::{room::encrypted::EncryptedEventContent, EventType}, identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId}, }; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tracing::warn; #[cfg(test)] -use super::{Account, OlmMachine}; +use crate::{Account, OlmMachine}; use crate::{ error::{EventError, OlmError, OlmResult, SignatureError}, - store::Result as StoreResult, - user_identity::{OwnUserIdentity, UserIdentities}, + identities::{OwnUserIdentity, UserIdentities}, + olm::Utility, + store::{caches::ReadOnlyUserDevices, Result as StoreResult}, verification::VerificationMachine, - verify_json, ReadOnlyUserDevices, Sas, + Sas, ToDeviceRequest, }; /// A read-only version of a `Device`. @@ -79,7 +79,7 @@ impl Device { /// Start a interactive verification with this `Device` /// /// Returns a `Sas` object and to-device request that needs to be sent out. - pub async fn start_verification(&self) -> StoreResult<(Sas, OwnedToDeviceRequest)> { + pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> { self.verification_machine .start_sas(self.inner.clone()) .await @@ -230,7 +230,7 @@ impl UserDevices { } } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] /// The local trust state of a device. pub enum LocalTrust { /// The device has been verified and is trusted. @@ -363,7 +363,9 @@ impl ReadOnlyDevice { .get_key(DeviceKeyAlgorithm::Ed25519) .ok_or(SignatureError::MissingSigningKey)?; - verify_json( + let utility = Utility::new(); + + utility.verify_json( &self.user_id, &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()), signing_key, @@ -443,7 +445,7 @@ pub(crate) mod test { use serde_json::json; use std::convert::TryFrom; - use crate::device::{LocalTrust, ReadOnlyDevice}; + use crate::identities::{LocalTrust, ReadOnlyDevice}; use matrix_sdk_common::{ encryption::DeviceKeys, identifiers::{user_id, DeviceKeyAlgorithm}, diff --git a/matrix_sdk_crypto/src/identities/mod.rs b/matrix_sdk_crypto/src/identities/mod.rs new file mode 100644 index 00000000..853447dc --- /dev/null +++ b/matrix_sdk_crypto/src/identities/mod.rs @@ -0,0 +1,50 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Collection of public identities used in Matrix. +//! +//! Matrix supports two main types of identities, a per-device identity and a +//! per-user identity. +//! +//! ## Device +//! +//! Every E2EE capable Matrix client will create a new Olm account and upload +//! the public keys of the Olm account to the server. This is represented as a +//! `ReadOnlyDevice`. +//! +//! Devices can have a local trust state which is needs to be saved in our +//! `CryptoStore`, to avoid reference cycles a wrapper for the `ReadOnlyDevice` +//! exists which adds methods to manipulate the local trust state. +//! +//! ## User +//! +//! Cross-signing capable devices will upload 3 additional (master, self-signing, +//! user-signing) public keys which represent the user identity owning all the +//! devices. This is represented in two ways, as a `UserIdentity` for other +//! users and as `OwnUserIdentity` for our own user. +//! +//! This is done because the server will only give us access to 2 of the 3 +//! additional public keys for other users, while it will give us access to all +//! 3 for our own user. +//! +//! Both identity sets need to reqularly fetched from the server using the +//! `/keys/query` API call. +pub(crate) mod device; +mod user; + +pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices}; +pub use user::{ + MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity, + UserSigningPubkey, +}; diff --git a/matrix_sdk_crypto/src/user_identity.rs b/matrix_sdk_crypto/src/identities/user.rs similarity index 97% rename from matrix_sdk_crypto/src/user_identity.rs rename to matrix_sdk_crypto/src/identities/user.rs index 4594b6f9..1444e7ac 100644 --- a/matrix_sdk_crypto/src/user_identity.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -20,6 +20,7 @@ use std::{ }, }; +use serde::{Deserialize, Serialize}; use serde_json::to_value; use matrix_sdk_common::{ @@ -27,25 +28,25 @@ use matrix_sdk_common::{ identifiers::{DeviceKeyId, UserId}, }; -use crate::{error::SignatureError, verify_json, ReadOnlyDevice}; +use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice}; /// Wrapper for a cross signing key marking it as the master key. /// /// Master keys are used to sign other cross signing keys, the self signing and /// user signing keys of an user will be signed by their master key. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct MasterPubkey(Arc); /// Wrapper for a cross signing key marking it as a self signing key. /// /// Self signing keys are used to sign the user's own devices. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SelfSigningPubkey(Arc); /// Wrapper for a cross signing key marking it as a user signing key. /// /// User signing keys are used to sign the master keys of other users. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserSigningPubkey(Arc); impl PartialEq for MasterPubkey { @@ -156,7 +157,8 @@ impl MasterPubkey { return Err(SignatureError::UserIdMissmatch); } - verify_json( + let utility = Utility::new(); + utility.verify_json( &self.0.user_id, &key_id, key, @@ -190,7 +192,8 @@ impl UserSigningPubkey { // TODO check that the usage is OK. - verify_json( + let utility = Utility::new(); + utility.verify_json( &self.0.user_id, &DeviceKeyId::try_from(key_id.as_str())?, key, @@ -223,7 +226,8 @@ impl SelfSigningPubkey { // TODO check that the usage is OK. - verify_json( + let utility = Utility::new(); + utility.verify_json( &self.0.user_id, &DeviceKeyId::try_from(key_id.as_str())?, key, @@ -279,7 +283,7 @@ impl PartialEq for UserIdentities { /// This is the user identity of a user that isn't our own. Other users will /// only contain a master key and a self signing key, meaning that only device /// signatures can be checked with this identity. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct UserIdentity { user_id: Arc, master_key: MasterPubkey, @@ -509,10 +513,10 @@ mod test { }; use crate::{ - device::{Device, ReadOnlyDevice}, + identities::{Device, ReadOnlyDevice}, machine::test::response_from_file, olm::Account, - store::memorystore::MemoryStore, + store::MemoryStore, verification::VerificationMachine, }; diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index bf05600f..edbe3067 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -27,98 +27,22 @@ )] #![cfg_attr(feature = "docs", feature(doc_cfg))] -mod device; mod error; +mod identities; #[allow(dead_code)] mod key_export; mod machine; -pub mod memory_stores; pub mod olm; mod requests; -mod store; -#[allow(dead_code)] -mod user_identity; +pub mod store; mod verification; -pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices}; pub use error::{MegolmError, OlmError}; +pub use identities::{ + Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, UserDevices, UserIdentities, UserIdentity, +}; pub use machine::OlmMachine; -use memory_stores::ReadOnlyUserDevices; pub(crate) use olm::Account; pub use olm::EncryptionSettings; -pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests}; -#[cfg(feature = "sqlite_cryptostore")] -pub use store::sqlite::SqliteStore; -pub use store::{CryptoStore, CryptoStoreError}; +pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests, ToDeviceRequest}; pub use verification::Sas; - -use error::SignatureError; -use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}; -use olm_rs::utility::OlmUtility; -use serde_json::Value; - -/// Verify a signed JSON object. -/// -/// The object must have a signatures key associated with an object of the -/// form `user_id: {key_id: signature}`. -/// -/// Returns Ok if the signature was successfully verified, otherwise an -/// SignatureError. -/// -/// # Arguments -/// -/// * `user_id` - The user who signed the JSON object. -/// -/// * `key_id` - The id of the key that signed the JSON object. -/// -/// * `signing_key` - The public ed25519 key which was used to sign the JSON -/// object. -/// -/// * `json` - The JSON object that should be verified. -pub(crate) fn verify_json( - user_id: &UserId, - key_id: &DeviceKeyId, - signing_key: &str, - json: &mut Value, -) -> Result<(), SignatureError> { - if key_id.algorithm() != DeviceKeyAlgorithm::Ed25519 { - return Err(SignatureError::UnsupportedAlgorithm); - } - - let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?; - let unsigned = json_object.remove("unsigned"); - let signatures = json_object.remove("signatures"); - - let canonical_json = cjson::to_string(json_object)?; - - if let Some(u) = unsigned { - json_object.insert("unsigned".to_string(), u); - } - - let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?; - let signature_object = signatures - .as_object() - .ok_or(SignatureError::NoSignatureFound)?; - let signature = signature_object - .get(user_id.as_str()) - .ok_or(SignatureError::NoSignatureFound)?; - let signature = signature - .get(key_id.to_string()) - .ok_or(SignatureError::NoSignatureFound)?; - let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?; - - let utility = OlmUtility::new(); - - let ret = if utility - .ed25519_verify(signing_key, &canonical_json, signature) - .is_ok() - { - Ok(()) - } else { - Err(SignatureError::VerificationError) - }; - - json_object.insert("signatures".to_string(), signatures); - - ret -} diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 77fbd492..a0935c27 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -34,9 +34,7 @@ use matrix_sdk_common::{ upload_keys, }, sync::sync_events::Response as SyncResponse, - to_device::{ - send_event_to_device::IncomingRequest as OwnedToDeviceRequest, DeviceIdOrAllDevices, - }, + to_device::DeviceIdOrAllDevices, }, encryption::DeviceKeys, events::{ @@ -53,20 +51,18 @@ use matrix_sdk_common::{ #[cfg(feature = "sqlite_cryptostore")] use super::store::sqlite::SqliteStore; use super::{ - device::{Device, ReadOnlyDevice, UserDevices}, error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult}, + identities::{ + Device, MasterPubkey, OwnUserIdentity, ReadOnlyDevice, SelfSigningPubkey, UserDevices, + UserIdentities, UserIdentity, UserSigningPubkey, + }, olm::{ Account, EncryptionSettings, GroupSessionKey, IdentityKeys, InboundGroupSession, OlmMessage, OutboundGroupSession, }, - requests::{IncomingResponse, OutgoingRequest}, - store::{memorystore::MemoryStore, Result as StoreResult}, - user_identity::{ - MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity, - UserSigningPubkey, - }, + requests::{IncomingResponse, OutgoingRequest, ToDeviceRequest}, + store::{CryptoStore, MemoryStore, Result as StoreResult}, verification::{Sas, VerificationMachine}, - CryptoStore, }; /// State machine implementation of the Olm/Megolm encryption protocol used for @@ -1127,7 +1123,7 @@ impl OlmMachine { room_id: &RoomId, users: impl Iterator, encryption_settings: impl Into, - ) -> OlmResult> { + ) -> OlmResult> { self.create_outbound_group_session(room_id, encryption_settings.into()) .await?; let session = self.outbound_group_sessions.get(room_id).unwrap(); @@ -1181,9 +1177,9 @@ impl OlmMachine { ); } - requests.push(OwnedToDeviceRequest { + requests.push(ToDeviceRequest { event_type: EventType::RoomEncrypted, - txn_id: Uuid::new_v4().to_string(), + txn_id: Uuid::new_v4(), messages, }); } @@ -1521,9 +1517,7 @@ impl OlmMachine { let own_identity = self .store .get_user_identity(self.user_id()) - .await - .ok() - .flatten() + .await? .map(|i| i.own().cloned()) .flatten(); let device_owner_identity = self.store.get_user_identity(user_id).await.ok().flatten(); @@ -1554,15 +1548,13 @@ pub(crate) mod test { use crate::{ machine::OlmMachine, + olm::Utility, verification::test::{outgoing_request_to_event, request_to_event}, - verify_json, EncryptionSettings, ReadOnlyDevice, + EncryptionSettings, ReadOnlyDevice, ToDeviceRequest, }; use matrix_sdk_common::{ - api::r0::{ - keys::{claim_keys, get_keys, upload_keys, OneTimeKey}, - to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest, - }, + api::r0::keys::{claim_keys, get_keys, upload_keys, OneTimeKey}, events::{ room::{ encrypted::EncryptedEventContent, @@ -1612,7 +1604,7 @@ pub(crate) mod test { get_keys::Response::try_from(data).expect("Can't parse the keys upload response") } - fn to_device_requests_to_content(requests: Vec) -> EncryptedEventContent { + fn to_device_requests_to_content(requests: Vec) -> EncryptedEventContent { let to_device_request = &requests[0]; let content: Raw = serde_json::from_str( @@ -1793,7 +1785,8 @@ pub(crate) mod test { let identity_keys = machine.account.identity_keys(); let ed25519_key = identity_keys.ed25519(); - let ret = verify_json( + let utility = Utility::new(); + let ret = utility.verify_json( &machine.user_id, &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), ed25519_key, @@ -1824,7 +1817,8 @@ pub(crate) mod test { let mut device_keys = machine.account.device_keys().await; - let ret = verify_json( + let utility = Utility::new(); + let ret = utility.verify_json( &machine.user_id, &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), "fake_key", @@ -1844,7 +1838,8 @@ pub(crate) mod test { let mut one_time_key = one_time_keys.values_mut().next().unwrap(); - let ret = verify_json( + let utility = Utility::new(); + let ret = utility.verify_json( &machine.user_id, &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), ed25519_key, @@ -1866,7 +1861,8 @@ pub(crate) mod test { .await .expect("Can't prepare initial key upload"); - let ret = verify_json( + let utility = Utility::new(); + let ret = utility.verify_json( &machine.user_id, &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), ed25519_key, @@ -1874,7 +1870,8 @@ pub(crate) mod test { ); assert!(ret.is_ok()); - let ret = verify_json( + let utility = Utility::new(); + let ret = utility.verify_json( &machine.user_id, &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), ed25519_key, diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 595a8ad0..e46c7142 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -26,7 +26,8 @@ use matrix_sdk_common::{ api::r0::keys::{OneTimeKey, SignedKey}, encryption::DeviceKeys, identifiers::{ - DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, + UserId, }, instant::Instant, locks::Mutex, @@ -36,6 +37,7 @@ use olm_rs::{ errors::{OlmAccountError, OlmSessionError}, PicklingMode, }; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; pub use olm_rs::{ @@ -45,7 +47,7 @@ pub use olm_rs::{ }; use super::{EncryptionSettings, InboundGroupSession, OutboundGroupSession, Session}; -use crate::{device::ReadOnlyDevice, error::SessionCreationError}; +use crate::{error::SessionCreationError, identities::ReadOnlyDevice}; /// Account holding identity keys for which sessions can be created. /// @@ -65,6 +67,42 @@ pub struct Account { uploaded_signed_key_count: Arc, } +/// A typed representation of a base64 encoded string containing the account +/// pickle. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AccountPickle(String); + +impl AccountPickle { + /// Get the string representation of the pickle. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl From for AccountPickle { + fn from(value: String) -> Self { + Self(value) + } +} + +/// A pickled version of an `Account`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// an account. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PickledAccount { + /// The user id of the account owner. + pub user_id: UserId, + /// The device id of the account owner. + pub device_id: DeviceIdBox, + /// The pickled version of the Olm account. + pub pickle: AccountPickle, + /// Was the account shared. + pub shared: bool, + /// The number of uploaded one-time keys we have on the server. + pub uploaded_signed_key_count: i64, +} + #[cfg(not(tarpaulin_include))] impl fmt::Debug for Account { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -240,40 +278,40 @@ impl Account { /// /// * `pickle_mode` - The mode that was used to pickle the account, either an /// unencrypted mode or an encrypted using passphrase. - pub async fn pickle(&self, pickle_mode: PicklingMode) -> String { - self.inner.lock().await.pickle(pickle_mode) + pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledAccount { + let pickle = AccountPickle(self.inner.lock().await.pickle(pickle_mode)); + + PickledAccount { + user_id: self.user_id().to_owned(), + device_id: self.device_id().to_owned(), + pickle, + shared: self.shared(), + uploaded_signed_key_count: self.uploaded_key_count(), + } } - /// Restore an account from a previously pickled string. + /// Restore an account from a previously pickled one. /// /// # Arguments /// - /// * `pickle` - The pickled string of the account. + /// * `pickle` - The pickled version of the Account. /// /// * `pickle_mode` - The mode that was used to pickle the account, either an /// unencrypted mode or an encrypted using passphrase. - /// - /// * `shared` - Boolean determining if the account was uploaded to the - /// server. - #[allow(clippy::ptr_arg)] pub fn from_pickle( - pickle: String, + pickle: PickledAccount, pickle_mode: PicklingMode, - shared: bool, - uploaded_signed_key_count: i64, - user_id: &UserId, - device_id: &DeviceId, ) -> Result { - let account = OlmAccount::unpickle(pickle, pickle_mode)?; + let account = OlmAccount::unpickle(pickle.pickle.0, pickle_mode)?; let identity_keys = account.parsed_identity_keys(); Ok(Account { - user_id: Arc::new(user_id.to_owned()), - device_id: Arc::new(device_id.into()), + user_id: Arc::new(pickle.user_id), + device_id: Arc::new(pickle.device_id), inner: Arc::new(Mutex::new(account)), identity_keys: Arc::new(identity_keys), - shared: Arc::new(AtomicBool::from(shared)), - uploaded_signed_key_count: Arc::new(AtomicI64::new(uploaded_signed_key_count)), + shared: Arc::new(AtomicBool::from(pickle.shared)), + uploaded_signed_key_count: Arc::new(AtomicI64::new(pickle.uploaded_signed_key_count)), }) } diff --git a/matrix_sdk_crypto/src/olm/group_sessions.rs b/matrix_sdk_crypto/src/olm/group_sessions.rs index e314e912..729bf5c6 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions.rs @@ -37,7 +37,7 @@ use olm_rs::{ errors::OlmGroupSessionError, inbound_group_session::OlmInboundGroupSession, outbound_group_session::OlmOutboundGroupSession, PicklingMode, }; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use zeroize::Zeroize; @@ -154,8 +154,16 @@ impl InboundGroupSession { /// /// * `pickle_mode` - The mode that was used to pickle the group session, /// either an unencrypted mode or an encrypted using passphrase. - pub async fn pickle(&self, pickle_mode: PicklingMode) -> String { - self.inner.lock().await.pickle(pickle_mode) + pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledInboundGroupSession { + let pickle = self.inner.lock().await.pickle(pickle_mode); + + PickledInboundGroupSession { + pickle: InboundGroupSessionPickle::from(pickle), + sender_key: self.sender_key.to_string(), + signing_key: self.signing_key.to_string(), + room_id: (&*self.room_id).clone(), + forwarding_chains: self.forwarding_chains.lock().await.clone(), + } } /// Restore a Session from a previously pickled string. @@ -165,35 +173,24 @@ impl InboundGroupSession { /// /// # Arguments /// - /// * `pickle` - The pickled string of the group session session. + /// * `pickle` - The pickled version of the `InboundGroupSession`. /// /// * `pickle_mode` - The mode that was used to pickle the session, either /// an unencrypted mode or an encrypted using passphrase. - /// - /// * `sender_key` - The public curve25519 key of the account that - /// sent us the session - /// - /// * `signing_key` - The public ed25519 key of the account that - /// sent us the session. - /// - /// * `room_id` - The id of the room that the session is used in. pub fn from_pickle( - pickle: String, + pickle: PickledInboundGroupSession, pickle_mode: PicklingMode, - sender_key: String, - signing_key: String, - room_id: RoomId, ) -> Result { - let session = OlmInboundGroupSession::unpickle(pickle, pickle_mode)?; + let session = OlmInboundGroupSession::unpickle(pickle.pickle.0, pickle_mode)?; let session_id = session.session_id(); Ok(InboundGroupSession { inner: Arc::new(Mutex::new(session)), session_id: Arc::new(session_id), - sender_key: Arc::new(sender_key), - signing_key: Arc::new(signing_key), - room_id: Arc::new(room_id), - forwarding_chains: Arc::new(Mutex::new(None)), + sender_key: Arc::new(pickle.sender_key), + signing_key: Arc::new(pickle.signing_key), + room_id: Arc::new(pickle.room_id), + forwarding_chains: Arc::new(Mutex::new(pickle.forwarding_chains)), }) } @@ -282,6 +279,42 @@ impl PartialEq for InboundGroupSession { } } +/// A pickled version of an `InboundGroupSession`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// an InboundGroupSession. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PickledInboundGroupSession { + /// The pickle string holding the InboundGroupSession. + pub pickle: InboundGroupSessionPickle, + /// The public curve25519 key of the account that sent us the session + pub sender_key: String, + /// The public ed25519 key of the account that sent us the session. + pub signing_key: String, + /// The id of the room that the session is used in. + pub room_id: RoomId, + /// The list of claimed ed25519 that forwarded us this key. Will be None if + /// we dirrectly received this session. + pub forwarding_chains: Option>, +} + +/// The typed representation of a base64 encoded string of the GroupSession pickle. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InboundGroupSessionPickle(String); + +impl From for InboundGroupSessionPickle { + fn from(pickle_string: String) -> Self { + InboundGroupSessionPickle(pickle_string) + } +} + +impl InboundGroupSessionPickle { + /// Get the string representation of the pickle. + pub fn as_str(&self) -> &str { + &self.0 + } +} + /// Outbound group session. /// /// Outbound group sessions are used to exchange room messages between a group diff --git a/matrix_sdk_crypto/src/olm/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index af597d95..c69293c5 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -20,12 +20,17 @@ mod account; mod group_sessions; mod session; +mod utility; -pub use account::{Account, IdentityKeys}; -pub use group_sessions::{EncryptionSettings, InboundGroupSession}; +pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount}; +pub use group_sessions::{ + EncryptionSettings, InboundGroupSession, InboundGroupSessionPickle, PickledInboundGroupSession, +}; pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; +pub use olm_rs::PicklingMode; pub(crate) use session::OlmMessage; -pub use session::Session; +pub use session::{PickledSession, Session, SessionPickle}; +pub(crate) use utility::Utility; #[cfg(test)] pub(crate) mod test { diff --git a/matrix_sdk_crypto/src/olm/session.rs b/matrix_sdk_crypto/src/olm/session.rs index 01191e75..554e75cb 100644 --- a/matrix_sdk_crypto/src/olm/session.rs +++ b/matrix_sdk_crypto/src/olm/session.rs @@ -20,15 +20,16 @@ use matrix_sdk_common::{ EventType, }, identifiers::{DeviceId, DeviceKeyAlgorithm, UserId}, - instant::Instant, + instant::{Duration, Instant}, locks::Mutex, }; use olm_rs::{errors::OlmSessionError, session::OlmSession, PicklingMode}; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use super::IdentityKeys; use crate::{ - error::{EventError, OlmResult}, + error::{EventError, OlmResult, SessionUnpicklingError}, ReadOnlyDevice, }; @@ -177,14 +178,22 @@ impl Session { /// /// * `pickle_mode` - The mode that was used to pickle the session, either /// an unencrypted mode or an encrypted using passphrase. - pub async fn pickle(&self, pickle_mode: PicklingMode) -> String { - self.inner.lock().await.pickle(pickle_mode) + pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledSession { + let pickle = self.inner.lock().await.pickle(pickle_mode); + + PickledSession { + pickle: SessionPickle::from(pickle), + sender_key: self.sender_key.to_string(), + // FIXME this should use the duration from the unix epoch. + creation_time: self.creation_time.elapsed(), + last_use_time: self.last_use_time.elapsed(), + } } /// Restore a Session from a previously pickled string. /// - /// Returns the restored Olm Session or a `OlmSessionError` if there was an - /// error. + /// Returns the restored Olm Session or a `SessionUnpicklingError` if there + /// was an error. /// /// # Arguments /// @@ -194,40 +203,37 @@ impl Session { /// /// * `our_idenity_keys` - An clone of the Arc to our own identity keys. /// - /// * `pickle` - The pickled string of the session. + /// * `pickle` - The pickled version of the `Session`. /// /// * `pickle_mode` - The mode that was used to pickle the session, either /// an unencrypted mode or an encrypted using passphrase. - /// - /// * `sender_key` - The public curve25519 key of the account that - /// established the session with us. - /// - /// * `creation_time` - The timestamp that marks when the session was - /// created. - /// - /// * `last_use_time` - The timestamp that marks when the session was - /// last used to encrypt or decrypt an Olm message. - #[allow(clippy::too_many_arguments)] pub fn from_pickle( user_id: Arc, device_id: Arc>, our_identity_keys: Arc, - pickle: String, + pickle: PickledSession, pickle_mode: PicklingMode, - sender_key: String, - creation_time: Instant, - last_use_time: Instant, - ) -> Result { - let session = OlmSession::unpickle(pickle, pickle_mode)?; + ) -> Result { + let session = OlmSession::unpickle(pickle.pickle.0, pickle_mode)?; let session_id = session.session_id(); + // FIXME this should use the UNIX epoch. + let now = Instant::now(); + + let creation_time = now + .checked_sub(pickle.creation_time) + .ok_or(SessionUnpicklingError::SessionTimestampError)?; + let last_use_time = now + .checked_sub(pickle.last_use_time) + .ok_or(SessionUnpicklingError::SessionTimestampError)?; + Ok(Session { user_id, device_id, our_identity_keys, inner: Arc::new(Mutex::new(session)), session_id: Arc::new(session_id), - sender_key: Arc::new(sender_key), + sender_key: Arc::new(pickle.sender_key), creation_time: Arc::new(creation_time), last_use_time: Arc::new(last_use_time), }) @@ -239,3 +245,36 @@ impl PartialEq for Session { self.session_id() == other.session_id() } } + +/// A pickled version of a `Session`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// a Session. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PickledSession { + /// The pickle string holding the Olm Session. + pub pickle: SessionPickle, + /// The curve25519 key of the other user that we share this session with. + pub sender_key: String, + /// The relative time elapsed since the session was created. + pub creation_time: Duration, + /// The relative time elapsed since the session was last used. + pub last_use_time: Duration, +} + +/// The typed representation of a base64 encoded string of the Olm Session pickle. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SessionPickle(String); + +impl From for SessionPickle { + fn from(picle_string: String) -> Self { + SessionPickle(picle_string) + } +} + +impl SessionPickle { + /// Get the string representation of the pickle. + pub fn as_str(&self) -> &str { + &self.0 + } +} diff --git a/matrix_sdk_crypto/src/olm/utility.rs b/matrix_sdk_crypto/src/olm/utility.rs new file mode 100644 index 00000000..afa73a6b --- /dev/null +++ b/matrix_sdk_crypto/src/olm/utility.rs @@ -0,0 +1,98 @@ +// Copyright 2020 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use olm_rs::utility::OlmUtility; +use serde_json::Value; + +use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}; + +use crate::error::SignatureError; + +pub(crate) struct Utility { + inner: OlmUtility, +} + +impl Utility { + pub fn new() -> Self { + Self { + inner: OlmUtility::new(), + } + } + + /// Verify a signed JSON object. + /// + /// The object must have a signatures key associated with an object of the + /// form `user_id: {key_id: signature}`. + /// + /// Returns Ok if the signature was successfully verified, otherwise an + /// SignatureError. + /// + /// # Arguments + /// + /// * `user_id` - The user who signed the JSON object. + /// + /// * `key_id` - The id of the key that signed the JSON object. + /// + /// * `signing_key` - The public ed25519 key which was used to sign the JSON + /// object. + /// + /// * `json` - The JSON object that should be verified. + pub(crate) fn verify_json( + &self, + user_id: &UserId, + key_id: &DeviceKeyId, + signing_key: &str, + json: &mut Value, + ) -> Result<(), SignatureError> { + if key_id.algorithm() != DeviceKeyAlgorithm::Ed25519 { + return Err(SignatureError::UnsupportedAlgorithm); + } + + let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?; + let unsigned = json_object.remove("unsigned"); + let signatures = json_object.remove("signatures"); + + let canonical_json = cjson::to_string(json_object)?; + + if let Some(u) = unsigned { + json_object.insert("unsigned".to_string(), u); + } + + let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?; + let signature_object = signatures + .as_object() + .ok_or(SignatureError::NoSignatureFound)?; + let signature = signature_object + .get(user_id.as_str()) + .ok_or(SignatureError::NoSignatureFound)?; + let signature = signature + .get(key_id.to_string()) + .ok_or(SignatureError::NoSignatureFound)?; + let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?; + + let ret = if self + .inner + .ed25519_verify(signing_key, &canonical_json, signature) + .is_ok() + { + Ok(()) + } else { + Err(SignatureError::VerificationError) + }; + + json_object.insert("signatures".to_string(), signatures); + + ret + } +} diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index a63d86a0..342f0b32 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::Arc; +use std::{collections::BTreeMap, sync::Arc}; use matrix_sdk_common::{ api::r0::{ @@ -21,13 +21,41 @@ use matrix_sdk_common::{ get_keys::{IncomingRequest as KeysQueryRequest, Response as KeysQueryResponse}, upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse}, }, - to_device::send_event_to_device::{ - IncomingRequest as ToDeviceRequest, Response as ToDeviceResponse, - }, + to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices}, }, + events::EventType, + identifiers::UserId, uuid::Uuid, }; +use serde_json::value::RawValue as RawJsonValue; + +/// Customized version of `ruma_client_api::r0::to_device::send_event_to_device::Request`, using a +/// UUID for the transaction ID. +#[derive(Clone, Debug)] +pub struct ToDeviceRequest { + /// Type of event being sent to each device. + pub event_type: EventType, + + /// A request identifier unique to the access token used to send the request. + pub txn_id: Uuid, + + /// A map of users to devices to a content for a message event to be + /// sent to the user's device. Individual message events can be sent + /// to devices, but all events must be of the same type. + /// The content's type for this field will be updated in a future + /// release, until then you can create a value using + /// `serde_json::value::to_raw_value`. + pub messages: BTreeMap>>, +} + +impl ToDeviceRequest { + /// Gets the transaction ID as a string. + pub fn txn_id_string(&self) -> String { + self.txn_id.to_string() + } +} + /// Enum over the different outgoing requests we can have. #[derive(Debug)] pub enum OutgoingRequests { diff --git a/matrix_sdk_crypto/src/memory_stores.rs b/matrix_sdk_crypto/src/store/caches.rs similarity index 98% rename from matrix_sdk_crypto/src/memory_stores.rs rename to matrix_sdk_crypto/src/store/caches.rs index 5fcf71f3..91344257 100644 --- a/matrix_sdk_crypto/src/memory_stores.rs +++ b/matrix_sdk_crypto/src/store/caches.rs @@ -25,8 +25,8 @@ use matrix_sdk_common::{ locks::Mutex, }; -use super::{ - device::ReadOnlyDevice, +use crate::{ + identities::ReadOnlyDevice, olm::{InboundGroupSession, Session}, }; @@ -208,9 +208,9 @@ impl DeviceStore { #[cfg(test)] mod test { use crate::{ - device::test::get_device, - memory_stores::{DeviceStore, GroupSessionStore, SessionStore}, + identities::device::test::get_device, olm::{test::get_account_and_session, InboundGroupSession}, + store::caches::{DeviceStore, GroupSessionStore, SessionStore}, }; use matrix_sdk_common::identifiers::room_id; diff --git a/matrix_sdk_crypto/src/store/memorystore.rs b/matrix_sdk_crypto/src/store/memorystore.rs index 2bccfb38..f7d3d753 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -14,19 +14,20 @@ use std::{collections::HashSet, sync::Arc}; -use async_trait::async_trait; use dashmap::{DashMap, DashSet}; use matrix_sdk_common::{ identifiers::{DeviceId, RoomId, UserId}, locks::Mutex, }; +use matrix_sdk_common_macros::async_trait; -use super::{Account, CryptoStore, InboundGroupSession, Result, Session}; -use crate::{ - device::ReadOnlyDevice, - memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, - user_identity::UserIdentities, +use super::{ + caches::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, + Account, CryptoStore, InboundGroupSession, Result, Session, }; +use crate::identities::{ReadOnlyDevice, UserIdentities}; + +/// An in-memory only store that will forget all the E2EE key once it's dropped. #[derive(Debug, Clone)] pub struct MemoryStore { sessions: SessionStore, @@ -37,8 +38,8 @@ pub struct MemoryStore { identities: Arc>, } -impl MemoryStore { - pub fn new() -> Self { +impl Default for MemoryStore { + fn default() -> Self { MemoryStore { sessions: SessionStore::new(), inbound_group_sessions: GroupSessionStore::new(), @@ -50,6 +51,13 @@ impl MemoryStore { } } +impl MemoryStore { + /// Create a new empty `MemoryStore`. + pub fn new() -> Self { + Self::default() + } +} + #[async_trait] impl CryptoStore for MemoryStore { async fn load_account(&self) -> Result> { @@ -153,7 +161,7 @@ impl CryptoStore for MemoryStore { #[cfg(test)] mod test { use crate::{ - device::test::get_device, + identities::device::test::get_device, olm::{test::get_account_and_session, InboundGroupSession}, store::{memorystore::MemoryStore, CryptoStore}, }; diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 0bea21fc..a7cce545 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -12,37 +12,73 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{collections::HashSet, io::Error as IoError, sync::Arc}; +//! Types and traits to implement the storage layer for the [`OlmMachine`] +//! +//! The storage layer for the [`OlmMachine`] can be customized using a trait. +//! Implementing your own [`CryptoStore`] +//! +//! An in-memory only store is provided as well as a SQLite based one, depending +//! on your needs and targets a custom store may be implemented, e.g. for +//! `wasm-unknown-unknown` an indexeddb store would be needed +//! +//! ``` +//! # use matrix_sdk_crypto::{ +//! # OlmMachine, +//! # store::MemoryStore, +//! # }; +//! # use matrix_sdk_common::identifiers::{user_id, DeviceIdBox}; +//! # let user_id = user_id!("@example:localhost"); +//! # let device_id: DeviceIdBox = "TEST".into(); +//! let store = Box::new(MemoryStore::new()); +//! +//! let machine = OlmMachine::new_with_store(user_id, device_id, store); +//! ``` +//! +//! [`OlmMachine`]: /matrix_sdk_crypto/struct.OlmMachine.html +//! [`CryptoStore`]: trait.Cryptostore.html + +pub mod caches; +mod memorystore; +#[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "sqlite_cryptostore")] +pub(crate) mod sqlite; + +use caches::ReadOnlyUserDevices; +pub use memorystore::MemoryStore; +#[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "sqlite_cryptostore")] +pub use sqlite::SqliteStore; + +use std::{collections::HashSet, fmt::Debug, io::Error as IoError, sync::Arc}; -use async_trait::async_trait; -use core::fmt::Debug; -use matrix_sdk_common::{ - identifiers::{DeviceId, RoomId, UserId}, - locks::Mutex, -}; -use matrix_sdk_common_macros::send_sync; use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError}; use serde_json::Error as SerdeError; use thiserror::Error; use url::ParseError; -use super::{ - device::ReadOnlyDevice, - memory_stores::ReadOnlyUserDevices, - olm::{Account, InboundGroupSession, Session}, - user_identity::UserIdentities, -}; - -pub mod memorystore; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(feature = "sqlite_cryptostore")] -pub mod sqlite; - +#[cfg_attr(feature = "docs", doc(cfg(r#sqlite_cryptostore)))] #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "sqlite_cryptostore")] use sqlx::Error as SqlxError; +use matrix_sdk_common::{ + identifiers::{DeviceId, Error as IdentifierValidationError, RoomId, UserId}, + locks::Mutex, +}; +use matrix_sdk_common_macros::async_trait; +#[cfg(not(target_arch = "wasm32"))] +use matrix_sdk_common_macros::send_sync; + +use super::{ + identities::{ReadOnlyDevice, UserIdentities}, + olm::{Account, InboundGroupSession, Session}, +}; + +use crate::error::SessionUnpicklingError; + +/// A `CryptoStore` specific result type. +pub type Result = std::result::Result; + #[derive(Error, Debug)] /// The crypto store's error type. pub enum CryptoStoreError { @@ -75,8 +111,12 @@ pub enum CryptoStoreError { OlmGroupSession(#[from] OlmGroupSessionError), /// A session time-stamp couldn't be loaded. - #[error("can't load session timestamps")] - SessionTimestampError, + #[error(transparent)] + SessionUnpickling(#[from] SessionUnpicklingError), + + /// A Matirx identifier failed to be validated. + #[error(transparent)] + IdentifierValidation(#[from] IdentifierValidationError), /// The store failed to (de)serialize a data type. #[error(transparent)] @@ -87,13 +127,11 @@ pub enum CryptoStoreError { UrlParse(#[from] ParseError), } -pub type Result = std::result::Result; - +/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic +/// keys. #[async_trait] #[allow(clippy::type_complexity)] #[cfg_attr(not(target_arch = "wasm32"), send_sync)] -/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic -/// keys. pub trait CryptoStore: Debug { /// Load an account that was previously stored. async fn load_account(&self) -> Result>; diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 3e2e6352..50b0b5f4 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -26,24 +26,29 @@ use matrix_sdk_common::{ identifiers::{ DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId, }, - instant::{Duration, Instant}, + instant::Duration, locks::Mutex, }; -use olm_rs::PicklingMode; use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection}; use url::Url; use zeroize::Zeroizing; -use super::{CryptoStore, CryptoStoreError, Result}; +use super::{ + caches::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, + CryptoStore, CryptoStoreError, Result, +}; use crate::{ - device::{LocalTrust, ReadOnlyDevice}, - memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, - olm::{Account, IdentityKeys, InboundGroupSession, Session}, - user_identity::UserIdentities, + identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, + olm::{ + Account, AccountPickle, IdentityKeys, InboundGroupSession, InboundGroupSessionPickle, + PickledAccount, PickledInboundGroupSession, PickledSession, PicklingMode, Session, + SessionPickle, + }, }; /// SQLite based implementation of a `CryptoStore`. #[derive(Clone)] +#[cfg_attr(feature = "docs", doc(cfg(r#sqlite_cryptostore)))] pub struct SqliteStore { user_id: Arc, device_id: Arc>, @@ -336,7 +341,7 @@ impl SqliteStore { .ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; - let rows: Vec<(String, String, String, String)> = query_as( + let mut rows: Vec<(String, String, String, String)> = query_as( "SELECT pickle, sender_key, creation_time, last_use_time FROM sessions WHERE account_id = ? and sender_key = ?", ) @@ -345,29 +350,27 @@ impl SqliteStore { .fetch_all(&mut *connection) .await?; - let now = Instant::now(); - Ok(rows - .iter() + .drain(..) .map(|row| { - let pickle = &row.0; - let sender_key = &row.1; - let creation_time = now - .checked_sub(serde_json::from_str::(&row.2)?) - .ok_or(CryptoStoreError::SessionTimestampError)?; - let last_use_time = now - .checked_sub(serde_json::from_str::(&row.3)?) - .ok_or(CryptoStoreError::SessionTimestampError)?; + let pickle = row.0; + let sender_key = row.1; + let creation_time = serde_json::from_str::(&row.2)?; + let last_use_time = serde_json::from_str::(&row.3)?; + + let pickle = PickledSession { + pickle: SessionPickle::from(pickle), + last_use_time, + creation_time, + sender_key, + }; Ok(Session::from_pickle( self.user_id.clone(), self.device_id.clone(), account_info.identity_keys.clone(), - pickle.to_string(), + pickle, self.get_pickle_mode(), - sender_key.to_string(), - creation_time, - last_use_time, )?) }) .collect::>>()?) @@ -377,7 +380,7 @@ impl SqliteStore { let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; - let rows: Vec<(String, String, String, String)> = query_as( + let mut rows: Vec<(String, String, String, String)> = query_as( "SELECT pickle, sender_key, signing_key, room_id FROM inbound_group_sessions WHERE account_id = ?", ) @@ -386,19 +389,26 @@ impl SqliteStore { .await?; let mut group_sessions = rows - .iter() + .drain(..) .map(|row| { - let pickle = &row.0; - let sender_key = &row.1; - let signing_key = &row.2; - let room_id = &row.3; + let pickle = row.0; + let sender_key = row.1; + let signing_key = row.2; + let room_id = row.3; + + let pickle = PickledInboundGroupSession { + pickle: InboundGroupSessionPickle::from(pickle), + sender_key, + signing_key, + room_id: RoomId::try_from(room_id)?, + // Fixme we need to store/restore these once we get support + // for key requesting/forwarding. + forwarding_chains: None, + }; Ok(InboundGroupSession::from_pickle( - pickle.to_string(), + pickle, self.get_pickle_mode(), - sender_key.to_string(), - signing_key.to_owned(), - RoomId::try_from(room_id.as_str()).unwrap(), )?) }) .collect::>>()?; @@ -546,15 +556,13 @@ impl SqliteStore { let signature = row.2; - if !signatures.contains_key(&user_id) { - let _ = signatures.insert(user_id.clone(), BTreeMap::new()); - } - let user_map = signatures.get_mut(&user_id).unwrap(); - - user_map.insert( - DeviceKeyId::from_parts(key_algorithm, device_id.as_str().into()), - signature.to_owned(), - ); + signatures + .entry(user_id) + .or_insert_with(BTreeMap::new) + .insert( + DeviceKeyId::from_parts(key_algorithm, device_id.as_str().into()), + signature.to_owned(), + ); } let device = ReadOnlyDevice::new( @@ -679,14 +687,15 @@ impl CryptoStore for SqliteStore { .await?; let result = if let Some((id, pickle, shared, uploaded_key_count)) = row { - let account = Account::from_pickle( - pickle, - self.get_pickle_mode(), + let pickle = PickledAccount { + user_id: (&*self.user_id).clone(), + device_id: (&*self.device_id).clone(), + pickle: AccountPickle::from(pickle), shared, - uploaded_key_count, - &self.user_id, - &self.device_id, - )?; + uploaded_signed_key_count: uploaded_key_count, + }; + + let account = Account::from_pickle(pickle, self.get_pickle_mode())?; *self.account_info.lock().unwrap() = Some(AccountInfo { account_id: id, @@ -720,18 +729,18 @@ impl CryptoStore for SqliteStore { shared = excluded.shared ", ) - .bind(&*self.user_id.to_string()) - .bind(&*self.device_id.to_string()) - .bind(&pickle) - .bind(account.shared()) - .bind(account.uploaded_key_count()) + .bind(pickle.user_id.as_str()) + .bind(pickle.device_id.as_str()) + .bind(pickle.pickle.as_str()) + .bind(pickle.shared) + .bind(pickle.uploaded_signed_key_count) .execute(&mut *connection) .await?; let account_id: (i64,) = query_as("SELECT id FROM accounts WHERE user_id = ? and device_id = ?") - .bind(&*self.user_id.to_string()) - .bind(&*self.device_id.to_string()) + .bind(self.user_id.as_str()) + .bind(self.device_id.as_str()) .fetch_one(&mut *connection) .await?; @@ -751,11 +760,12 @@ impl CryptoStore for SqliteStore { self.lazy_load_sessions(&session.sender_key).await?; self.sessions.add(session.clone()).await; - let session_id = session.session_id(); - let creation_time = serde_json::to_string(&session.creation_time.elapsed())?; - let last_use_time = serde_json::to_string(&session.last_use_time.elapsed())?; let pickle = session.pickle(self.get_pickle_mode()).await; + let session_id = session.session_id(); + let creation_time = serde_json::to_string(&pickle.creation_time)?; + let last_use_time = serde_json::to_string(&pickle.last_use_time)?; + let mut connection = self.connection.lock().await; query( @@ -767,8 +777,8 @@ impl CryptoStore for SqliteStore { .bind(&account_id) .bind(&*creation_time) .bind(&*last_use_time) - .bind(&*session.sender_key) - .bind(&pickle) + .bind(&pickle.sender_key) + .bind(&pickle.pickle.as_str()) .execute(&mut *connection) .await?; } @@ -786,6 +796,10 @@ impl CryptoStore for SqliteStore { let mut connection = self.connection.lock().await; let session_id = session.session_id(); + // FIXME we need to store/restore the forwarding chains. + // FIXME this should be converted so it accepts an array of sessions for + // the key import feature. + query( "INSERT INTO inbound_group_sessions ( session_id, account_id, sender_key, signing_key, @@ -797,10 +811,10 @@ impl CryptoStore for SqliteStore { ) .bind(session_id) .bind(account_id) - .bind(&*session.sender_key) - .bind(&*session.signing_key) - .bind(&*session.room_id.to_string()) - .bind(&pickle) + .bind(pickle.sender_key) + .bind(pickle.signing_key) + .bind(pickle.room_id.as_str()) + .bind(pickle.pickle.as_str()) .execute(&mut *connection) .await?; @@ -908,7 +922,7 @@ impl std::fmt::Debug for SqliteStore { #[cfg(test)] mod test { use crate::{ - device::test::get_device, + identities::device::test::get_device, olm::{Account, GroupSessionKey, InboundGroupSession, Session}, }; use matrix_sdk_common::{ @@ -921,27 +935,37 @@ mod test { use super::{CryptoStore, SqliteStore}; - fn example_user_id() -> UserId { - user_id!("@example:localhost") + fn alice_id() -> UserId { + user_id!("@alice:example.org") } - fn example_device_id() -> &'static DeviceId { - "DEVICEID".into() + fn alice_device_id() -> Box { + "ALICEDEVICE".into() + } + + fn bob_id() -> UserId { + user_id!("@bob:example.org") + } + + fn bob_device_id() -> Box { + "BOBDEVICE".into() } async fn get_store(passphrase: Option<&str>) -> (SqliteStore, tempfile::TempDir) { let tmpdir = tempdir().unwrap(); let tmpdir_path = tmpdir.path().to_str().unwrap(); - let user_id = &example_user_id(); - let device_id = example_device_id(); - let store = if let Some(passphrase) = passphrase { - SqliteStore::open_with_passphrase(&user_id, device_id, tmpdir_path, passphrase) - .await - .expect("Can't create a passphrase protected store") + SqliteStore::open_with_passphrase( + &alice_id(), + &alice_device_id(), + tmpdir_path, + passphrase, + ) + .await + .expect("Can't create a passphrase protected store") } else { - SqliteStore::open(&user_id, device_id, tmpdir_path) + SqliteStore::open(&alice_id(), &alice_device_id(), tmpdir_path) .await .expect("Can't create store") }; @@ -960,22 +984,6 @@ mod test { (account, store, dir) } - fn alice_id() -> UserId { - user_id!("@alice:example.org") - } - - fn alice_device_id() -> Box { - "ALICEDEVICE".into() - } - - fn bob_id() -> UserId { - user_id!("@bob:example.org") - } - - fn bob_device_id() -> Box { - "BOBDEVICE".into() - } - fn get_account() -> Account { Account::new(&alice_id(), &alice_device_id()) } @@ -1011,7 +1019,7 @@ mod test { async fn create_store() { let tmpdir = tempdir().unwrap(); let tmpdir_path = tmpdir.path().to_str().unwrap(); - let _ = SqliteStore::open(&example_user_id(), "DEVICEID".into(), tmpdir_path) + let _ = SqliteStore::open(&alice_id(), &alice_device_id(), tmpdir_path) .await .expect("Can't create store"); } @@ -1138,7 +1146,7 @@ mod test { drop(store); - let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path()) + let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path()) .await .expect("Can't create store"); @@ -1224,7 +1232,7 @@ mod test { assert!(store.users_for_key_query().contains(device.user_id())); drop(store); - let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path()) + let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path()) .await .expect("Can't create store"); @@ -1239,7 +1247,7 @@ mod test { .unwrap(); assert!(!store.users_for_key_query().contains(device.user_id())); - let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path()) + let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path()) .await .expect("Can't create store"); @@ -1257,7 +1265,7 @@ mod test { drop(store); - let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path()) + let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path()) .await .expect("Can't create store"); @@ -1290,7 +1298,7 @@ mod test { store.save_devices(&[device.clone()]).await.unwrap(); store.delete_device(device.clone()).await.unwrap(); - let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path()) + let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path()) .await .expect("Can't create store"); diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 898c5830..9dbaca6c 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -19,14 +19,17 @@ use dashmap::DashMap; use tracing::{trace, warn}; use matrix_sdk_common::{ - api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest, events::{AnyToDeviceEvent, AnyToDeviceEventContent}, identifiers::{DeviceId, UserId}, uuid::Uuid, }; use super::sas::{content_to_request, Sas}; -use crate::{requests::OutgoingRequest, Account, CryptoStore, CryptoStoreError, ReadOnlyDevice}; +use crate::{ + requests::{OutgoingRequest, ToDeviceRequest}, + store::{CryptoStore, CryptoStoreError}, + Account, ReadOnlyDevice, +}; #[derive(Clone, Debug)] pub struct VerificationMachine { @@ -49,7 +52,7 @@ impl VerificationMachine { pub async fn start_sas( &self, device: ReadOnlyDevice, - ) -> Result<(Sas, OwnedToDeviceRequest), CryptoStoreError> { + ) -> Result<(Sas, ToDeviceRequest), CryptoStoreError> { let identity = self.store.get_user_identity(device.user_id()).await?; let (sas, content) = Sas::start( @@ -59,7 +62,7 @@ impl VerificationMachine { identity, ); - let (_, request) = content_to_request( + let request = content_to_request( device.user_id(), device.device_id(), AnyToDeviceEventContent::KeyVerificationStart(content), @@ -82,7 +85,8 @@ impl VerificationMachine { recipient_device: &DeviceId, content: AnyToDeviceEventContent, ) { - let (request_id, request) = content_to_request(recipient, recipient_device, content); + let request = content_to_request(recipient, recipient_device, content); + let request_id = request.txn_id; let request = OutgoingRequest { request_id, @@ -117,10 +121,10 @@ impl VerificationMachine { for sas in self.verifications.iter() { if let Some(r) = sas.cancel_if_timed_out() { self.outgoing_to_device_messages.insert( - r.0, + r.txn_id, OutgoingRequest { - request_id: r.0, - request: Arc::new(r.1.into()), + request_id: r.txn_id, + request: Arc::new(r.into()), }, ); } @@ -193,10 +197,10 @@ impl VerificationMachine { if !s.mark_device_as_verified().await? { if let Some(r) = s.cancel() { self.outgoing_to_device_messages.insert( - r.0, + r.txn_id, OutgoingRequest { - request_id: r.0, - request: Arc::new(r.1.into()), + request_id: r.txn_id, + request: Arc::new(r.into()), }, ); } @@ -229,9 +233,9 @@ mod test { use super::{Sas, VerificationMachine}; use crate::{ requests::OutgoingRequests, - store::memorystore::MemoryStore, + store::{CryptoStore, MemoryStore}, verification::test::{get_content_from_request, wrap_any_to_device_content}, - Account, CryptoStore, ReadOnlyDevice, + Account, ReadOnlyDevice, }; fn alice_id() -> UserId { diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index ab79c0f7..7083c760 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -20,19 +20,15 @@ pub use sas::Sas; #[cfg(test)] pub(crate) mod test { - use crate::requests::{OutgoingRequest, OutgoingRequests}; + use crate::requests::{OutgoingRequest, OutgoingRequests, ToDeviceRequest}; use serde_json::Value; use matrix_sdk_common::{ - api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest, events::{AnyToDeviceEvent, AnyToDeviceEventContent, EventType, ToDeviceEvent}, identifiers::UserId, }; - pub(crate) fn request_to_event( - sender: &UserId, - request: &OwnedToDeviceRequest, - ) -> AnyToDeviceEvent { + pub(crate) fn request_to_event(sender: &UserId, request: &ToDeviceRequest) -> AnyToDeviceEvent { let content = get_content_from_request(request); wrap_any_to_device_content(sender, content) } @@ -81,9 +77,7 @@ pub(crate) mod test { } } - pub(crate) fn get_content_from_request( - request: &OwnedToDeviceRequest, - ) -> AnyToDeviceEventContent { + pub(crate) fn get_content_from_request(request: &ToDeviceRequest) -> AnyToDeviceEventContent { let json: Value = serde_json::from_str( request .messages diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 259248e1..d7abf88d 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -19,9 +19,7 @@ use tracing::{trace, warn}; use olm_rs::sas::OlmSas; use matrix_sdk_common::{ - api::r0::to_device::{ - send_event_to_device::IncomingRequest as OwnedToDeviceRequest, DeviceIdOrAllDevices, - }, + api::r0::to_device::DeviceIdOrAllDevices, events::{ key::verification::{cancel::CancelCode, mac::MacEventContent}, AnyToDeviceEventContent, EventType, ToDeviceEvent, @@ -30,7 +28,10 @@ use matrix_sdk_common::{ uuid::Uuid, }; -use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice}; +use crate::{ + identities::{ReadOnlyDevice, UserIdentities}, + Account, ToDeviceRequest, +}; #[derive(Clone, Debug)] pub struct SasIds { @@ -464,7 +465,7 @@ pub fn content_to_request( recipient: &UserId, recipient_device: &DeviceId, content: AnyToDeviceEventContent, -) -> (Uuid, OwnedToDeviceRequest) { +) -> ToDeviceRequest { let mut messages = BTreeMap::new(); let mut user_messages = BTreeMap::new(); @@ -483,16 +484,11 @@ pub fn content_to_request( _ => unreachable!(), }; - let request_id = Uuid::new_v4(); - - ( - request_id, - OwnedToDeviceRequest { - txn_id: request_id.to_string(), - event_type, - messages, - }, - ) + ToDeviceRequest { + txn_id: Uuid::new_v4(), + event_type, + messages, + } } #[cfg(test)] diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 3e4db5e3..61724ed5 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -22,7 +22,6 @@ use std::sync::{Arc, Mutex}; use tracing::{info, trace, warn}; use matrix_sdk_common::{ - api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest, events::{ key::verification::{ accept::AcceptEventContent, cancel::CancelCode, mac::MacEventContent, @@ -31,12 +30,12 @@ use matrix_sdk_common::{ AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, identifiers::{DeviceId, UserId}, - uuid::Uuid, }; use crate::{ - user_identity::UserIdentities, Account, CryptoStore, CryptoStoreError, LocalTrust, - ReadOnlyDevice, + identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, + store::{CryptoStore, CryptoStoreError}, + Account, ToDeviceRequest, }; pub use helpers::content_to_request; @@ -166,10 +165,10 @@ impl Sas { /// /// This does nothing if the verification was already accepted, otherwise it /// returns an `AcceptEventContent` that needs to be sent out. - pub fn accept(&self) -> Option { + pub fn accept(&self) -> Option { self.inner.lock().unwrap().accept().map(|c| { let content = AnyToDeviceEventContent::KeyVerificationAccept(c); - self.content_to_request(content).1 + self.content_to_request(content) }) } @@ -180,7 +179,7 @@ impl Sas { /// Does nothing if we're not in a state where we can confirm the short auth /// string, otherwise returns a `MacEventContent` that needs to be sent to /// the server. - pub async fn confirm(&self) -> Result, CryptoStoreError> { + pub async fn confirm(&self) -> Result, CryptoStoreError> { let (content, done) = { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); @@ -195,7 +194,7 @@ impl Sas { // else branch and only after the identity was verified as well. We // dont' want to verify one without the other. if !self.mark_device_as_verified().await? { - return Ok(self.cancel().map(|r| r.1)); + return Ok(self.cancel()); } else { self.mark_identity_as_verified().await?; } @@ -203,7 +202,7 @@ impl Sas { Ok(content.map(|c| { let content = AnyToDeviceEventContent::KeyVerificationMac(c); - self.content_to_request(content).1 + self.content_to_request(content) })) } @@ -328,7 +327,7 @@ impl Sas { /// /// Returns None if the `Sas` object is already in a canceled state, /// otherwise it returns a request that needs to be sent out. - pub fn cancel(&self) -> Option<(Uuid, OwnedToDeviceRequest)> { + pub fn cancel(&self) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.cancel(CancelCode::User); @@ -337,7 +336,7 @@ impl Sas { content.map(|c| self.content_to_request(c)) } - pub(crate) fn cancel_if_timed_out(&self) -> Option<(Uuid, OwnedToDeviceRequest)> { + pub(crate) fn cancel_if_timed_out(&self) -> Option { if self.is_canceled() || self.is_done() { None } else if self.timed_out() { @@ -408,10 +407,7 @@ impl Sas { self.inner.lock().unwrap().verified_identities() } - pub(crate) fn content_to_request( - &self, - content: AnyToDeviceEventContent, - ) -> (Uuid, OwnedToDeviceRequest) { + pub(crate) fn content_to_request(&self, content: AnyToDeviceEventContent) -> ToDeviceRequest { content_to_request(self.other_user_id(), self.other_device_id(), content) } } @@ -658,9 +654,9 @@ mod test { }; use crate::{ - store::memorystore::MemoryStore, + store::{CryptoStore, MemoryStore}, verification::test::{get_content_from_request, wrap_any_to_device_content}, - Account, CryptoStore, ReadOnlyDevice, + Account, ReadOnlyDevice, }; use super::{Accepted, Created, Sas, SasState, Started}; diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 85201fa9..8d6ede7f 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -43,7 +43,10 @@ use matrix_sdk_common::{ use super::helpers::{get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds}; -use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice}; +use crate::{ + identities::{ReadOnlyDevice, UserIdentities}, + Account, +}; const KEY_AGREEMENT_PROTOCOLS: &[KeyAgreementProtocol] = &[KeyAgreementProtocol::Curve25519HkdfSha256];