From 5c4e46e908cf255dbb901d15feec8ff866c80f31 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Wed, 26 Aug 2020 16:26:40 -0400 Subject: [PATCH 01/20] matrix_sdk_common: Bump ruma --- matrix_sdk_common/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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] From 6872cc717b8b7746ea4c4bec8d143e8653f965ed Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Wed, 26 Aug 2020 16:30:29 -0400 Subject: [PATCH 02/20] matrix_sdk: fix Client docs for methods that used request builders --- matrix_sdk/src/client.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 73d0051b..5daaeade 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -514,7 +514,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 +741,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 +779,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 +814,7 @@ impl Client { /// # Arguments /// /// * `request` - The easiest way to create this request is using the - /// `MessagesRequestBuilder`. + /// `get_message_events::Request` itself. /// /// # Examples /// ```no_run From 0de4a21320364f772e04a2bbcabae436715e61b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 1 Sep 2020 17:37:01 +0200 Subject: [PATCH 03/20] crypto: Expose some missing structs that are needed to implement a cryptostore. --- matrix_sdk_crypto/src/lib.rs | 3 ++- matrix_sdk_crypto/src/olm/account.rs | 3 ++- matrix_sdk_crypto/src/olm/mod.rs | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 38630f32..451d5f4a 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -41,13 +41,14 @@ mod verification; pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices}; pub use error::{MegolmError, OlmError}; pub use machine::OlmMachine; -use memory_stores::ReadOnlyUserDevices; +pub 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 user_identity::{OwnUserIdentity, UserIdentities, UserIdentity}; pub use verification::Sas; use error::SignatureError; diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 595a8ad0..74c42e08 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, diff --git a/matrix_sdk_crypto/src/olm/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index af597d95..495a51f9 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -24,6 +24,7 @@ mod session; pub use account::{Account, IdentityKeys}; pub use group_sessions::{EncryptionSettings, InboundGroupSession}; pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; +pub use olm_rs::PicklingMode; pub(crate) use session::OlmMessage; pub use session::Session; From 987d87cd5d84e20d48cd72f5198416c5c427c745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 1 Sep 2020 17:41:30 +0200 Subject: [PATCH 04/20] crypto: Use the correct async-trait macro for the CryptoStores. --- matrix_sdk_crypto/src/store/memorystore.rs | 2 +- matrix_sdk_crypto/src/store/mod.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/matrix_sdk_crypto/src/store/memorystore.rs b/matrix_sdk_crypto/src/store/memorystore.rs index 2bccfb38..087f720a 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -14,12 +14,12 @@ 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::{ diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 0bea21fc..4a9a4044 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -12,15 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{collections::HashSet, io::Error as IoError, sync::Arc}; +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::async_trait; +#[cfg(not(target_arch = "wasm32"))] use matrix_sdk_common_macros::send_sync; + use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError}; use serde_json::Error as SerdeError; use thiserror::Error; @@ -89,11 +90,11 @@ pub enum CryptoStoreError { 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>; From 269cfc3d342356c8fe8bd86b53d33fd5399a91c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 09:37:10 +0200 Subject: [PATCH 05/20] crypto: Add a pickled account struct making account storing easier. --- matrix_sdk_crypto/src/olm/account.rs | 73 +++++++++++++++----- matrix_sdk_crypto/src/olm/mod.rs | 2 +- matrix_sdk_crypto/src/store/sqlite.rs | 95 +++++++++++++-------------- 3 files changed, 102 insertions(+), 68 deletions(-) diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 74c42e08..d199f81d 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -37,6 +37,7 @@ use olm_rs::{ errors::{OlmAccountError, OlmSessionError}, PicklingMode, }; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; pub use olm_rs::{ @@ -66,6 +67,42 @@ pub struct Account { uploaded_signed_key_count: Arc, } +#[derive(Debug, Clone, Serialize, Deserialize)] +/// A typed representation of a base64 encoded string containing the account +/// pickle. +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) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +/// A pickled version of an `Account`. +/// +/// Holds all the information that needs to be stored in a database to restore +/// an account. +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 { @@ -241,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/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index 495a51f9..0feafbc1 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -21,7 +21,7 @@ mod account; mod group_sessions; mod session; -pub use account::{Account, IdentityKeys}; +pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount}; pub use group_sessions::{EncryptionSettings, InboundGroupSession}; pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; pub use olm_rs::PicklingMode; diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 3e2e6352..52b302dc 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -29,7 +29,6 @@ use matrix_sdk_common::{ instant::{Duration, Instant}, locks::Mutex, }; -use olm_rs::PicklingMode; use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection}; use url::Url; use zeroize::Zeroizing; @@ -38,7 +37,10 @@ use super::{CryptoStore, CryptoStoreError, Result}; use crate::{ device::{LocalTrust, ReadOnlyDevice}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, - olm::{Account, IdentityKeys, InboundGroupSession, Session}, + olm::{ + Account, AccountPickle, IdentityKeys, InboundGroupSession, PickledAccount, PicklingMode, + Session, + }, user_identity::UserIdentities, }; @@ -679,14 +681,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 +723,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?; @@ -921,27 +924,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 +973,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 +1008,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 +1135,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 +1221,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 +1236,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 +1254,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 +1287,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"); From 81b127b6e78d24dfd0b8f237066d91a37bbbeb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 11:45:35 +0200 Subject: [PATCH 06/20] crypto: Modify all the pickling logic so we return serializeable structs. --- matrix_sdk_crypto/src/olm/account.rs | 4 +- matrix_sdk_crypto/src/olm/group_sessions.rs | 59 ++++++++++++--- matrix_sdk_crypto/src/olm/mod.rs | 6 +- matrix_sdk_crypto/src/olm/session.rs | 77 ++++++++++++++----- matrix_sdk_crypto/src/store/sqlite.rs | 83 +++++++++++---------- 5 files changed, 155 insertions(+), 74 deletions(-) diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index d199f81d..0292afdd 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -67,9 +67,9 @@ pub struct Account { uploaded_signed_key_count: Arc, } -#[derive(Debug, Clone, Serialize, Deserialize)] /// A typed representation of a base64 encoded string containing the account /// pickle. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct AccountPickle(String); impl AccountPickle { @@ -85,11 +85,11 @@ impl From for AccountPickle { } } -#[derive(Debug, Clone, Serialize, Deserialize)] /// 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, diff --git a/matrix_sdk_crypto/src/olm/group_sessions.rs b/matrix_sdk_crypto/src/olm/group_sessions.rs index e314e912..8781d751 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,15 @@ 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(), + } } /// Restore a Session from a previously pickled string. @@ -178,21 +185,18 @@ impl InboundGroupSession { /// /// * `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), + 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(None)), }) } @@ -282,6 +286,39 @@ 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 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 0feafbc1..3694b7c8 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -22,11 +22,13 @@ mod group_sessions; mod session; pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount}; -pub use group_sessions::{EncryptionSettings, InboundGroupSession}; +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}; #[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..f8545d58 100644 --- a/matrix_sdk_crypto/src/olm/session.rs +++ b/matrix_sdk_crypto/src/olm/session.rs @@ -20,10 +20,11 @@ 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; @@ -177,8 +178,16 @@ 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. @@ -194,40 +203,35 @@ 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)?; + 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).unwrap(); + // .ok_or(CryptoStoreError::SessionTimestampError)?; + let last_use_time = now.checked_sub(pickle.last_use_time).unwrap(); + // .ok_or(CryptoStoreError::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 +243,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/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 52b302dc..e07f04cc 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -26,7 +26,7 @@ use matrix_sdk_common::{ identifiers::{ DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId, }, - instant::{Duration, Instant}, + instant::Duration, locks::Mutex, }; use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection}; @@ -38,14 +38,15 @@ use crate::{ device::{LocalTrust, ReadOnlyDevice}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, olm::{ - Account, AccountPickle, IdentityKeys, InboundGroupSession, PickledAccount, PicklingMode, - Session, + Account, AccountPickle, IdentityKeys, InboundGroupSession, InboundGroupSessionPickle, + PickledAccount, PickledInboundGroupSession, PickledSession, PicklingMode, Session, + SessionPickle, }, user_identity::UserIdentities, }; -/// SQLite based implementation of a `CryptoStore`. #[derive(Clone)] +/// SQLite based implementation of a `CryptoStore`. pub struct SqliteStore { user_id: Arc, device_id: Arc>, @@ -338,7 +339,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 = ?", ) @@ -347,29 +348,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::>>()?) @@ -379,7 +378,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 = ?", ) @@ -388,19 +387,24 @@ 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, + // FIXME remove this unwrap. + room_id: RoomId::try_from(room_id).unwrap(), + }; 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::>>()?; @@ -754,11 +758,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( @@ -770,8 +775,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?; } @@ -800,10 +805,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?; From 4bab678e4685889ba5539c3856b68fffd45046e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 11:49:49 +0200 Subject: [PATCH 07/20] crypto: Allow most of the ReadOnlyDevice to be serialized. --- matrix_sdk_crypto/Cargo.toml | 2 +- matrix_sdk_crypto/src/device.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index e1ec78b2..8d38d331 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -26,7 +26,7 @@ matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_mac 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/device.rs b/matrix_sdk_crypto/src/device.rs index bfbaface..687401aa 100644 --- a/matrix_sdk_crypto/src/device.rs +++ b/matrix_sdk_crypto/src/device.rs @@ -31,6 +31,7 @@ use matrix_sdk_common::{ events::{room::encrypted::EncryptedEventContent, EventType}, identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId}, }; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tracing::warn; @@ -230,7 +231,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. From c652762255576f1f292c6c98cd7d358d41b0e51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 11:54:04 +0200 Subject: [PATCH 08/20] crypto: Allow user identities to be seralized/deserialized. --- matrix_sdk_crypto/src/user_identity.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/matrix_sdk_crypto/src/user_identity.rs b/matrix_sdk_crypto/src/user_identity.rs index 4594b6f9..8b154a2f 100644 --- a/matrix_sdk_crypto/src/user_identity.rs +++ b/matrix_sdk_crypto/src/user_identity.rs @@ -20,6 +20,7 @@ use std::{ }, }; +use serde::{Deserialize, Serialize}; use serde_json::to_value; use matrix_sdk_common::{ @@ -33,19 +34,19 @@ use crate::{error::SignatureError, verify_json, ReadOnlyDevice}; /// /// 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 { @@ -233,7 +234,7 @@ impl SelfSigningPubkey { } /// Enum over the different user identity types we can have. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum UserIdentities { /// Our own user identity. Own(OwnUserIdentity), @@ -279,7 +280,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, @@ -370,7 +371,7 @@ impl UserIdentity { /// /// This identity can verify other identities as well as devices belonging to /// the identity. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct OwnUserIdentity { user_id: Arc, master_key: MasterPubkey, From 8c4acf54e0e46f3218c8fda0bc59e66026332840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 12:11:06 +0200 Subject: [PATCH 09/20] crypto: Reorder the errors so unpickling now returns the timestamp error. --- matrix_sdk_crypto/src/error.rs | 10 ++++++++++ matrix_sdk_crypto/src/olm/session.rs | 18 ++++++++++-------- matrix_sdk_crypto/src/store/mod.rs | 6 ++++-- 3 files changed, 24 insertions(+), 10 deletions(-) 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/olm/session.rs b/matrix_sdk_crypto/src/olm/session.rs index f8545d58..554e75cb 100644 --- a/matrix_sdk_crypto/src/olm/session.rs +++ b/matrix_sdk_crypto/src/olm/session.rs @@ -29,7 +29,7 @@ use serde_json::{json, Value}; use super::IdentityKeys; use crate::{ - error::{EventError, OlmResult}, + error::{EventError, OlmResult, SessionUnpicklingError}, ReadOnlyDevice, }; @@ -192,8 +192,8 @@ impl Session { /// 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 /// @@ -213,17 +213,19 @@ impl Session { our_identity_keys: Arc, pickle: PickledSession, pickle_mode: PicklingMode, - ) -> Result { + ) -> 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).unwrap(); - // .ok_or(CryptoStoreError::SessionTimestampError)?; - let last_use_time = now.checked_sub(pickle.last_use_time).unwrap(); - // .ok_or(CryptoStoreError::SessionTimestampError)?; + 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, diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 4a9a4044..3e66661c 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -34,6 +34,8 @@ use super::{ user_identity::UserIdentities, }; +use crate::error::SessionUnpicklingError; + pub mod memorystore; #[cfg(not(target_arch = "wasm32"))] @@ -76,8 +78,8 @@ 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), /// The store failed to (de)serialize a data type. #[error(transparent)] From 8b56546565f4cdeebfbab4f25f18f4772ba58ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 12:17:38 +0200 Subject: [PATCH 10/20] crypto: Remove an unwrap from the sqlite cryptostore. --- matrix_sdk_crypto/src/store/mod.rs | 6 +++++- matrix_sdk_crypto/src/store/sqlite.rs | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 3e66661c..edd044cb 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -15,7 +15,7 @@ use std::{collections::HashSet, fmt::Debug, io::Error as IoError, sync::Arc}; use matrix_sdk_common::{ - identifiers::{DeviceId, RoomId, UserId}, + identifiers::{DeviceId, Error as IdentifierValidationError, RoomId, UserId}, locks::Mutex, }; use matrix_sdk_common_macros::async_trait; @@ -81,6 +81,10 @@ pub enum CryptoStoreError { #[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)] Serialization(#[from] SerdeError), diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index e07f04cc..b3f567c2 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -398,8 +398,7 @@ impl SqliteStore { pickle: InboundGroupSessionPickle::from(pickle), sender_key, signing_key, - // FIXME remove this unwrap. - room_id: RoomId::try_from(room_id).unwrap(), + room_id: RoomId::try_from(room_id)?, }; Ok(InboundGroupSession::from_pickle( From 65843f89dc0011b3f50b9e3013ecc95cc94e9a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 12:24:46 +0200 Subject: [PATCH 11/20] crypto: Simplify the signature loading in the sqlite cryptostore. --- matrix_sdk_crypto/src/store/sqlite.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index b3f567c2..1e6577eb 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -551,15 +551,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( From 2195da1cd895726864fe8864870b40dfdbcbdb9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 12:28:18 +0200 Subject: [PATCH 12/20] crypto: Fix some docs. --- matrix_sdk_crypto/src/olm/group_sessions.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/matrix_sdk_crypto/src/olm/group_sessions.rs b/matrix_sdk_crypto/src/olm/group_sessions.rs index 8781d751..30d6aec9 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions.rs @@ -172,18 +172,10 @@ 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: PickledInboundGroupSession, pickle_mode: PicklingMode, From 8b5bb7d8c5620f78e47426d7f373bcc544191ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 13:54:04 +0200 Subject: [PATCH 13/20] crypto: Remove the deserialize implementations for our user identity. Deriving Serialize/Deserialize for an AtomicBool doesn't seem to be implemented under WASM. So remove the derives for now. --- matrix_sdk_crypto/Cargo.toml | 2 +- matrix_sdk_crypto/src/user_identity.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index 8d38d331..70f65023 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -20,7 +20,7 @@ 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" } diff --git a/matrix_sdk_crypto/src/user_identity.rs b/matrix_sdk_crypto/src/user_identity.rs index 8b154a2f..2c15ef09 100644 --- a/matrix_sdk_crypto/src/user_identity.rs +++ b/matrix_sdk_crypto/src/user_identity.rs @@ -234,7 +234,7 @@ impl SelfSigningPubkey { } /// Enum over the different user identity types we can have. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone)] pub enum UserIdentities { /// Our own user identity. Own(OwnUserIdentity), @@ -371,7 +371,7 @@ impl UserIdentity { /// /// This identity can verify other identities as well as devices belonging to /// the identity. -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Debug, Clone)] pub struct OwnUserIdentity { user_id: Arc, master_key: MasterPubkey, From cc236a876528adefad365bdb725325f71213ab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 14:23:00 +0200 Subject: [PATCH 14/20] examples: Fix the wasm bot example. --- matrix_sdk/examples/wasm_command_bot/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) 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::{ From d86c05efb355dec8f43fd8dbe88ed094f21686bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 2 Sep 2020 15:08:24 +0200 Subject: [PATCH 15/20] crypto: Add a fixme to the sqlite store since it's not storing forwarding chains. --- matrix_sdk_crypto/src/olm/group_sessions.rs | 6 +++++- matrix_sdk_crypto/src/store/sqlite.rs | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/olm/group_sessions.rs b/matrix_sdk_crypto/src/olm/group_sessions.rs index 30d6aec9..729bf5c6 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions.rs @@ -162,6 +162,7 @@ impl InboundGroupSession { 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(), } } @@ -189,7 +190,7 @@ impl InboundGroupSession { 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(None)), + forwarding_chains: Arc::new(Mutex::new(pickle.forwarding_chains)), }) } @@ -292,6 +293,9 @@ pub struct PickledInboundGroupSession { 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. diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 1e6577eb..4cd905f8 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -399,6 +399,9 @@ impl SqliteStore { 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( @@ -791,6 +794,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, From 73c104cac1a49818e7af52596d6e1f0e2781feff Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Thu, 3 Sep 2020 20:02:55 +0200 Subject: [PATCH 16/20] Replace IncomingToDeviceRequest with customized request type --- matrix_sdk/src/client.rs | 21 ++++++----- matrix_sdk/src/device.rs | 2 +- matrix_sdk/src/sas.rs | 8 ++--- matrix_sdk_base/src/client.rs | 5 ++- matrix_sdk_base/src/lib.rs | 2 +- matrix_sdk_crypto/src/device.rs | 8 ++--- matrix_sdk_crypto/src/lib.rs | 2 +- matrix_sdk_crypto/src/machine.rs | 21 +++++------ matrix_sdk_crypto/src/requests.rs | 36 ++++++++++++++++--- matrix_sdk_crypto/src/verification/machine.rs | 25 +++++++------ matrix_sdk_crypto/src/verification/mod.rs | 12 ++----- .../src/verification/sas/helpers.rs | 23 +++++------- matrix_sdk_crypto/src/verification/sas/mod.rs | 23 +++++------- 13 files changed, 96 insertions(+), 92 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 5daaeade..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, @@ -1101,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 @@ -1230,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 @@ -1320,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..07ef1795 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -39,7 +39,6 @@ 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, @@ -47,7 +46,7 @@ use matrix_sdk_common::{ #[cfg(feature = "encryption")] use matrix_sdk_crypto::{ CryptoStore, CryptoStoreError, Device, IncomingResponse, OlmError, OlmMachine, OutgoingRequest, - Sas, UserDevices, + Sas, ToDeviceRequest, UserDevices, }; use zeroize::Zeroizing; @@ -1304,7 +1303,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..abfaf827 100644 --- a/matrix_sdk_base/src/lib.rs +++ b/matrix_sdk_base/src/lib.rs @@ -58,7 +58,7 @@ pub use state::{AllRooms, ClientState}; #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub use matrix_sdk_crypto::{ CryptoStoreError, Device, IncomingResponse, LocalTrust, OutgoingRequest, OutgoingRequests, - ReadOnlyDevice, Sas, UserDevices, + ReadOnlyDevice, Sas, ToDeviceRequest, UserDevices, }; #[cfg(feature = "messages")] diff --git a/matrix_sdk_crypto/src/device.rs b/matrix_sdk_crypto/src/device.rs index 687401aa..0e1087eb 100644 --- a/matrix_sdk_crypto/src/device.rs +++ b/matrix_sdk_crypto/src/device.rs @@ -24,9 +24,7 @@ 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}, @@ -43,7 +41,7 @@ use crate::{ store::Result as StoreResult, user_identity::{OwnUserIdentity, UserIdentities}, verification::VerificationMachine, - verify_json, ReadOnlyUserDevices, Sas, + verify_json, ReadOnlyUserDevices, Sas, ToDeviceRequest, }; /// A read-only version of a `Device`. @@ -80,7 +78,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 diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 451d5f4a..af63b034 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -44,7 +44,7 @@ pub use machine::OlmMachine; pub use memory_stores::ReadOnlyUserDevices; pub(crate) use olm::Account; pub use olm::EncryptionSettings; -pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests}; +pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests, ToDeviceRequest}; #[cfg(feature = "sqlite_cryptostore")] pub use store::sqlite::SqliteStore; pub use store::{CryptoStore, CryptoStoreError}; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 77fbd492..baf36789 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::{ @@ -59,7 +57,7 @@ use super::{ Account, EncryptionSettings, GroupSessionKey, IdentityKeys, InboundGroupSession, OlmMessage, OutboundGroupSession, }, - requests::{IncomingResponse, OutgoingRequest}, + requests::{IncomingResponse, OutgoingRequest, ToDeviceRequest}, store::{memorystore::MemoryStore, Result as StoreResult}, user_identity::{ MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity, @@ -1127,7 +1125,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 +1179,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, }); } @@ -1555,14 +1553,11 @@ pub(crate) mod test { use crate::{ machine::OlmMachine, verification::test::{outgoing_request_to_event, request_to_event}, - verify_json, EncryptionSettings, ReadOnlyDevice, + verify_json, 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 +1607,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( 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/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 898c5830..0728a7b6 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -19,14 +19,16 @@ 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, Account, CryptoStore, CryptoStoreError, ReadOnlyDevice, + ToDeviceRequest, +}; #[derive(Clone, Debug)] pub struct VerificationMachine { @@ -49,7 +51,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 +61,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 +84,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 +120,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 +196,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()), }, ); } 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..262b4bac 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,7 @@ use matrix_sdk_common::{ uuid::Uuid, }; -use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice}; +use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice, ToDeviceRequest}; #[derive(Clone, Debug)] pub struct SasIds { @@ -464,7 +462,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 +481,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..aaf8ef86 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,11 @@ use matrix_sdk_common::{ AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, identifiers::{DeviceId, UserId}, - uuid::Uuid, }; use crate::{ user_identity::UserIdentities, Account, CryptoStore, CryptoStoreError, LocalTrust, - ReadOnlyDevice, + ReadOnlyDevice, ToDeviceRequest, }; pub use helpers::content_to_request; @@ -166,10 +164,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 +178,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 +193,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 +201,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 +326,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 +335,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 +406,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) } } From 7b3dfe2f27cd040179e7ebe084e25700a5206654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 4 Sep 2020 10:49:11 +0200 Subject: [PATCH 17/20] crypto: Move the device and user identities under one module. --- .../src/{ => identities}/device.rs | 6 +-- matrix_sdk_crypto/src/identities/mod.rs | 50 +++++++++++++++++++ .../{user_identity.rs => identities/user.rs} | 2 +- matrix_sdk_crypto/src/lib.rs | 9 ++-- matrix_sdk_crypto/src/machine.rs | 9 ++-- matrix_sdk_crypto/src/memory_stores.rs | 4 +- matrix_sdk_crypto/src/olm/account.rs | 2 +- matrix_sdk_crypto/src/store/memorystore.rs | 5 +- matrix_sdk_crypto/src/store/mod.rs | 3 +- matrix_sdk_crypto/src/store/sqlite.rs | 5 +- .../src/verification/sas/helpers.rs | 5 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 4 +- .../src/verification/sas/sas_state.rs | 5 +- 13 files changed, 80 insertions(+), 29 deletions(-) rename matrix_sdk_crypto/src/{ => identities}/device.rs (99%) create mode 100644 matrix_sdk_crypto/src/identities/mod.rs rename matrix_sdk_crypto/src/{user_identity.rs => identities/user.rs} (99%) diff --git a/matrix_sdk_crypto/src/device.rs b/matrix_sdk_crypto/src/identities/device.rs similarity index 99% rename from matrix_sdk_crypto/src/device.rs rename to matrix_sdk_crypto/src/identities/device.rs index 687401aa..925b7f83 100644 --- a/matrix_sdk_crypto/src/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -36,12 +36,12 @@ 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}, + identities::{OwnUserIdentity, UserIdentities}, store::Result as StoreResult, - user_identity::{OwnUserIdentity, UserIdentities}, verification::VerificationMachine, verify_json, ReadOnlyUserDevices, Sas, }; @@ -444,7 +444,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 99% rename from matrix_sdk_crypto/src/user_identity.rs rename to matrix_sdk_crypto/src/identities/user.rs index 2c15ef09..a1cd66aa 100644 --- a/matrix_sdk_crypto/src/user_identity.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -510,7 +510,7 @@ mod test { }; use crate::{ - device::{Device, ReadOnlyDevice}, + identities::{Device, ReadOnlyDevice}, machine::test::response_from_file, olm::Account, store::memorystore::MemoryStore, diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 451d5f4a..675e5668 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -27,19 +27,19 @@ )] #![cfg_attr(feature = "docs", feature(doc_cfg))] -mod device; mod error; +mod identities; mod machine; pub mod memory_stores; pub mod olm; mod requests; mod store; -#[allow(dead_code)] -mod user_identity; 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; pub use memory_stores::ReadOnlyUserDevices; pub(crate) use olm::Account; @@ -48,7 +48,6 @@ pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests}; #[cfg(feature = "sqlite_cryptostore")] pub use store::sqlite::SqliteStore; pub use store::{CryptoStore, CryptoStoreError}; -pub use user_identity::{OwnUserIdentity, UserIdentities, UserIdentity}; pub use verification::Sas; use error::SignatureError; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 77fbd492..0d9a8987 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -53,18 +53,17 @@ 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, - }, verification::{Sas, VerificationMachine}, CryptoStore, }; diff --git a/matrix_sdk_crypto/src/memory_stores.rs b/matrix_sdk_crypto/src/memory_stores.rs index 5fcf71f3..83ee06fb 100644 --- a/matrix_sdk_crypto/src/memory_stores.rs +++ b/matrix_sdk_crypto/src/memory_stores.rs @@ -26,7 +26,7 @@ use matrix_sdk_common::{ }; use super::{ - device::ReadOnlyDevice, + identities::ReadOnlyDevice, olm::{InboundGroupSession, Session}, }; @@ -208,7 +208,7 @@ impl DeviceStore { #[cfg(test)] mod test { use crate::{ - device::test::get_device, + identities::device::test::get_device, memory_stores::{DeviceStore, GroupSessionStore, SessionStore}, olm::{test::get_account_and_session, InboundGroupSession}, }; diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 0292afdd..e46c7142 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -47,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. /// diff --git a/matrix_sdk_crypto/src/store/memorystore.rs b/matrix_sdk_crypto/src/store/memorystore.rs index 087f720a..d43ee531 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -23,9 +23,8 @@ use matrix_sdk_common_macros::async_trait; use super::{Account, CryptoStore, InboundGroupSession, Result, Session}; use crate::{ - device::ReadOnlyDevice, + identities::{ReadOnlyDevice, UserIdentities}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, - user_identity::UserIdentities, }; #[derive(Debug, Clone)] pub struct MemoryStore { @@ -153,7 +152,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 edd044cb..9fc1dc04 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -28,10 +28,9 @@ use thiserror::Error; use url::ParseError; use super::{ - device::ReadOnlyDevice, + identities::{ReadOnlyDevice, UserIdentities}, memory_stores::ReadOnlyUserDevices, olm::{Account, InboundGroupSession, Session}, - user_identity::UserIdentities, }; use crate::error::SessionUnpicklingError; diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 4cd905f8..f29a4ce4 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -35,14 +35,13 @@ use zeroize::Zeroizing; use super::{CryptoStore, CryptoStoreError, Result}; use crate::{ - device::{LocalTrust, ReadOnlyDevice}, + identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, olm::{ Account, AccountPickle, IdentityKeys, InboundGroupSession, InboundGroupSessionPickle, PickledAccount, PickledInboundGroupSession, PickledSession, PicklingMode, Session, SessionPickle, }, - user_identity::UserIdentities, }; #[derive(Clone)] @@ -920,7 +919,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::{ diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 259248e1..848d37ef 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -30,7 +30,10 @@ use matrix_sdk_common::{ uuid::Uuid, }; -use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice}; +use crate::{ + identities::{ReadOnlyDevice, UserIdentities}, + Account, +}; #[derive(Clone, Debug)] pub struct SasIds { diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 3e4db5e3..dde0f1c6 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -35,8 +35,8 @@ use matrix_sdk_common::{ }; use crate::{ - user_identity::UserIdentities, Account, CryptoStore, CryptoStoreError, LocalTrust, - ReadOnlyDevice, + identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, + Account, CryptoStore, CryptoStoreError, }; pub use helpers::content_to_request; 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]; From adf8905d9fe2cb3b6bbb4fecfe76aba1880e992e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 4 Sep 2020 12:42:40 +0200 Subject: [PATCH 18/20] crypto: Rename the memory stores into caches and reorder the store module. --- matrix_sdk_base/src/client.rs | 4 +- matrix_sdk_base/src/lib.rs | 4 +- matrix_sdk_crypto/src/identities/device.rs | 4 +- matrix_sdk_crypto/src/identities/user.rs | 2 +- matrix_sdk_crypto/src/lib.rs | 7 +- matrix_sdk_crypto/src/machine.rs | 3 +- .../src/{memory_stores.rs => store/caches.rs} | 4 +- matrix_sdk_crypto/src/store/memorystore.rs | 21 ++++-- matrix_sdk_crypto/src/store/mod.rs | 66 ++++++++++++++----- matrix_sdk_crypto/src/store/sqlite.rs | 9 ++- matrix_sdk_crypto/src/verification/machine.rs | 10 ++- matrix_sdk_crypto/src/verification/sas/mod.rs | 7 +- 12 files changed, 92 insertions(+), 49 deletions(-) rename matrix_sdk_crypto/src/{memory_stores.rs => store/caches.rs} (99%) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 5f2e14ea..97532ada 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -46,8 +46,8 @@ use matrix_sdk_common::{ }; #[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, UserDevices, }; use zeroize::Zeroizing; diff --git a/matrix_sdk_base/src/lib.rs b/matrix_sdk_base/src/lib.rs index f96d1728..c1002046 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, UserDevices, }; #[cfg(feature = "messages")] diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 925b7f83..60b87925 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -41,9 +41,9 @@ use crate::{Account, OlmMachine}; use crate::{ error::{EventError, OlmError, OlmResult, SignatureError}, identities::{OwnUserIdentity, UserIdentities}, - store::Result as StoreResult, + store::{caches::ReadOnlyUserDevices, Result as StoreResult}, verification::VerificationMachine, - verify_json, ReadOnlyUserDevices, Sas, + verify_json, Sas, }; /// A read-only version of a `Device`. diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index a1cd66aa..7d7acfad 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -513,7 +513,7 @@ mod test { 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 675e5668..0280d408 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -30,10 +30,9 @@ mod error; mod identities; mod machine; -pub mod memory_stores; pub mod olm; mod requests; -mod store; +pub mod store; mod verification; pub use error::{MegolmError, OlmError}; @@ -41,13 +40,9 @@ pub use identities::{ Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, UserDevices, UserIdentities, UserIdentity, }; pub use machine::OlmMachine; -pub 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 verification::Sas; use error::SignatureError; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 0d9a8987..95a390bf 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -63,9 +63,8 @@ use super::{ OlmMessage, OutboundGroupSession, }, requests::{IncomingResponse, OutgoingRequest}, - store::{memorystore::MemoryStore, Result as StoreResult}, + store::{CryptoStore, MemoryStore, Result as StoreResult}, verification::{Sas, VerificationMachine}, - CryptoStore, }; /// State machine implementation of the Olm/Megolm encryption protocol used for diff --git a/matrix_sdk_crypto/src/memory_stores.rs b/matrix_sdk_crypto/src/store/caches.rs similarity index 99% rename from matrix_sdk_crypto/src/memory_stores.rs rename to matrix_sdk_crypto/src/store/caches.rs index 83ee06fb..91344257 100644 --- a/matrix_sdk_crypto/src/memory_stores.rs +++ b/matrix_sdk_crypto/src/store/caches.rs @@ -25,7 +25,7 @@ use matrix_sdk_common::{ locks::Mutex, }; -use super::{ +use crate::{ identities::ReadOnlyDevice, olm::{InboundGroupSession, Session}, }; @@ -209,8 +209,8 @@ impl DeviceStore { mod test { use crate::{ identities::device::test::get_device, - memory_stores::{DeviceStore, GroupSessionStore, SessionStore}, 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 d43ee531..f7d3d753 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -21,11 +21,13 @@ use matrix_sdk_common::{ }; use matrix_sdk_common_macros::async_trait; -use super::{Account, CryptoStore, InboundGroupSession, Result, Session}; -use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, - memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, +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, @@ -36,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(), @@ -49,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> { diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 9fc1dc04..a7cce545 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -12,8 +12,55 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! 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 olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError}; +use serde_json::Error as SerdeError; +use thiserror::Error; +use url::ParseError; + +#[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, @@ -22,28 +69,15 @@ use matrix_sdk_common_macros::async_trait; #[cfg(not(target_arch = "wasm32"))] 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::{ identities::{ReadOnlyDevice, UserIdentities}, - memory_stores::ReadOnlyUserDevices, olm::{Account, InboundGroupSession, Session}, }; use crate::error::SessionUnpicklingError; -pub mod memorystore; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(feature = "sqlite_cryptostore")] -pub mod sqlite; - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(feature = "sqlite_cryptostore")] -use sqlx::Error as SqlxError; +/// A `CryptoStore` specific result type. +pub type Result = std::result::Result; #[derive(Error, Debug)] /// The crypto store's error type. @@ -93,8 +127,6 @@ 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] diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index f29a4ce4..50b0b5f4 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -33,10 +33,12 @@ use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConn use url::Url; use zeroize::Zeroizing; -use super::{CryptoStore, CryptoStoreError, Result}; +use super::{ + caches::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, + CryptoStore, CryptoStoreError, Result, +}; use crate::{ identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, - memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore}, olm::{ Account, AccountPickle, IdentityKeys, InboundGroupSession, InboundGroupSessionPickle, PickledAccount, PickledInboundGroupSession, PickledSession, PicklingMode, Session, @@ -44,8 +46,9 @@ use crate::{ }, }; -#[derive(Clone)] /// 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>, diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 898c5830..65050ff2 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -26,7 +26,11 @@ use matrix_sdk_common::{ }; use super::sas::{content_to_request, Sas}; -use crate::{requests::OutgoingRequest, Account, CryptoStore, CryptoStoreError, ReadOnlyDevice}; +use crate::{ + requests::OutgoingRequest, + store::{CryptoStore, CryptoStoreError}, + Account, ReadOnlyDevice, +}; #[derive(Clone, Debug)] pub struct VerificationMachine { @@ -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/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index dde0f1c6..1be3a236 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -36,7 +36,8 @@ use matrix_sdk_common::{ use crate::{ identities::{LocalTrust, ReadOnlyDevice, UserIdentities}, - Account, CryptoStore, CryptoStoreError, + store::{CryptoStore, CryptoStoreError}, + Account, }; pub use helpers::content_to_request; @@ -658,9 +659,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}; From 53fec7a87e2f0d45719cc3bc3e91d24f222e0711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 4 Sep 2020 12:44:03 +0200 Subject: [PATCH 19/20] crypto: Don't ignore store errors when fetching the identities. --- matrix_sdk_crypto/src/machine.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 95a390bf..363c920c 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1519,9 +1519,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(); From 89efcee337443cd446e99f40b9ccf69edb16715e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 4 Sep 2020 13:14:01 +0200 Subject: [PATCH 20/20] crypto: Move the signature verification method under an Utility struct. --- matrix_sdk_crypto/src/identities/device.rs | 7 +- matrix_sdk_crypto/src/identities/user.rs | 11 ++- matrix_sdk_crypto/src/lib.rs | 71 ---------------- matrix_sdk_crypto/src/machine.rs | 18 ++-- matrix_sdk_crypto/src/olm/mod.rs | 2 + matrix_sdk_crypto/src/olm/utility.rs | 98 ++++++++++++++++++++++ 6 files changed, 124 insertions(+), 83 deletions(-) create mode 100644 matrix_sdk_crypto/src/olm/utility.rs diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 55fe5594..039e9c87 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -39,9 +39,10 @@ use crate::{Account, OlmMachine}; use crate::{ error::{EventError, OlmError, OlmResult, SignatureError}, identities::{OwnUserIdentity, UserIdentities}, + olm::Utility, store::{caches::ReadOnlyUserDevices, Result as StoreResult}, verification::VerificationMachine, - verify_json, Sas, ToDeviceRequest, + Sas, ToDeviceRequest, }; /// A read-only version of a `Device`. @@ -362,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, diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index 7d7acfad..1444e7ac 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -28,7 +28,7 @@ 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. /// @@ -157,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, @@ -191,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, @@ -224,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, diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 1ede88bb..959ffe97 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -44,74 +44,3 @@ pub(crate) use olm::Account; pub use olm::EncryptionSettings; 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 6a06acbe..a0935c27 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1548,8 +1548,9 @@ pub(crate) mod test { use crate::{ machine::OlmMachine, + olm::Utility, verification::test::{outgoing_request_to_event, request_to_event}, - verify_json, EncryptionSettings, ReadOnlyDevice, ToDeviceRequest, + EncryptionSettings, ReadOnlyDevice, ToDeviceRequest, }; use matrix_sdk_common::{ @@ -1784,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, @@ -1815,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", @@ -1835,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, @@ -1857,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, @@ -1865,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/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index 3694b7c8..c69293c5 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -20,6 +20,7 @@ mod account; mod group_sessions; mod session; +mod utility; pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount}; pub use group_sessions::{ @@ -29,6 +30,7 @@ pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession}; pub use olm_rs::PicklingMode; pub(crate) use session::OlmMessage; 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/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 + } +}