From e50cf39a17752879902b5ade1acc551a3c20c556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 21 Jul 2020 12:40:23 +0200 Subject: [PATCH] crypto: Store a copy of the user_id/device_id and identity keys in sessions. --- matrix_sdk_crypto/src/lib.rs | 2 +- matrix_sdk_crypto/src/olm/account.rs | 8 +++- matrix_sdk_crypto/src/olm/session.rs | 12 ++++- matrix_sdk_crypto/src/store/sqlite.rs | 67 ++++++++++++++++++--------- 4 files changed, 65 insertions(+), 24 deletions(-) diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 148480ca..332e61db 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -37,7 +37,7 @@ pub use device::{Device, TrustState}; pub use error::{MegolmError, OlmError}; pub use machine::{OlmMachine, OneTimeKeys}; pub use memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices}; -pub use olm::{Account, InboundGroupSession, OutboundGroupSession, Session}; +pub use olm::{Account, IdentityKeys, InboundGroupSession, OutboundGroupSession, Session}; #[cfg(feature = "sqlite-cryptostore")] pub use store::sqlite::SqliteStore; pub use store::{CryptoStore, CryptoStoreError}; diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 4cff7467..a1308666 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -52,7 +52,7 @@ pub struct Account { pub(crate) user_id: Arc, pub(crate) device_id: Arc>, inner: Arc>, - identity_keys: Arc, + pub(crate) identity_keys: Arc, shared: Arc, /// The number of signed one-time keys we have uploaded to the server. If /// this is None, no action will be taken. After a sync request the client @@ -395,6 +395,9 @@ impl Account { let session_id = session.session_id(); Ok(Session { + user_id: self.user_id.clone(), + device_id: self.device_id.clone(), + our_identity_keys: self.identity_keys.clone(), inner: Arc::new(Mutex::new(session)), session_id: Arc::new(session_id), sender_key: Arc::new(their_identity_key.to_owned()), @@ -495,6 +498,9 @@ impl Account { let session_id = session.session_id(); Ok(Session { + user_id: self.user_id.clone(), + device_id: self.device_id.clone(), + our_identity_keys: self.identity_keys.clone(), inner: Arc::new(Mutex::new(session)), session_id: Arc::new(session_id), sender_key: Arc::new(their_identity_key.to_owned()), diff --git a/matrix_sdk_crypto/src/olm/session.rs b/matrix_sdk_crypto/src/olm/session.rs index fff93eab..5bf9e6c2 100644 --- a/matrix_sdk_crypto/src/olm/session.rs +++ b/matrix_sdk_crypto/src/olm/session.rs @@ -27,7 +27,7 @@ pub use olm_rs::{ utility::OlmUtility, }; -use super::Account; +use super::{Account, IdentityKeys}; use crate::error::{EventError, OlmResult}; use crate::Device; @@ -37,6 +37,7 @@ use matrix_sdk_common::{ room::encrypted::{CiphertextInfo, EncryptedEventContent, OlmV1Curve25519AesSha2Content}, EventType, }, + identifiers::{DeviceId, UserId}, instant::Instant, locks::Mutex, }; @@ -45,6 +46,9 @@ use matrix_sdk_common::{ /// `Account`s #[derive(Clone)] pub struct Session { + pub(crate) user_id: Arc, + pub(crate) device_id: Arc>, + pub(crate) our_identity_keys: Arc, pub(crate) inner: Arc>, pub(crate) session_id: Arc, pub(crate) sender_key: Arc, @@ -197,6 +201,9 @@ impl Session { /// * `last_use_time` - The timestamp that marks when the session was /// last used to encrypt or decrypt an Olm message. pub fn from_pickle( + user_id: Arc, + device_id: Arc>, + our_identity_keys: Arc, pickle: String, pickle_mode: PicklingMode, sender_key: String, @@ -207,6 +214,9 @@ impl Session { let session_id = session.session_id(); 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), diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 5d69e466..bc382d6b 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -26,9 +26,10 @@ use olm_rs::PicklingMode; use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection}; use zeroize::Zeroizing; -use super::{Account, CryptoStore, CryptoStoreError, InboundGroupSession, Result, Session}; +use super::{CryptoStore, CryptoStoreError, Result}; use crate::device::{Device, TrustState}; use crate::memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices}; +use crate::{Account, IdentityKeys, InboundGroupSession, Session}; use matrix_sdk_common::api::r0::keys::{AlgorithmAndDeviceId, KeyAlgorithm}; use matrix_sdk_common::events::Algorithm; use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId}; @@ -36,8 +37,8 @@ use matrix_sdk_common::identifiers::{DeviceId, RoomId, UserId}; /// SQLite based implementation of a `CryptoStore`. pub struct SqliteStore { user_id: Arc, - device_id: Arc, - account_id: Option, + device_id: Arc>, + account_info: Option, path: PathBuf, sessions: SessionStore, @@ -50,6 +51,11 @@ pub struct SqliteStore { pickle_passphrase: Option>, } +struct AccountInfo { + account_id: i64, + identity_keys: Arc, +} + static DATABASE_NAME: &str = "matrix-sdk-crypto.db"; impl SqliteStore { @@ -118,8 +124,8 @@ impl SqliteStore { let connection = SqliteConnection::connect(url.as_ref()).await?; let store = SqliteStore { user_id: Arc::new(user_id.to_owned()), - device_id: Arc::new(device_id.to_owned()), - account_id: None, + device_id: Arc::new(device_id.into()), + account_info: None, sessions: SessionStore::new(), inbound_group_sessions: GroupSessionStore::new(), devices: DeviceStore::new(), @@ -133,6 +139,10 @@ impl SqliteStore { Ok(store) } + fn account_id(&self) -> Option { + self.account_info.as_ref().map(|i| i.account_id) + } + async fn create_tables(&self) -> Result<()> { let mut connection = self.connection.lock().await; connection @@ -288,14 +298,17 @@ impl SqliteStore { } async fn load_sessions_for(&mut self, sender_key: &str) -> Result> { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_info = self + .account_info + .as_ref() + .ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; let 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 = ?", ) - .bind(account_id) + .bind(account_info.account_id) .bind(sender_key) .fetch_all(&mut *connection) .await?; @@ -315,6 +328,9 @@ impl SqliteStore { .ok_or(CryptoStoreError::SessionTimestampError)?; Ok(Session::from_pickle( + self.user_id.clone(), + self.device_id.clone(), + account_info.identity_keys.clone(), pickle.to_string(), self.get_pickle_mode(), sender_key.to_string(), @@ -326,7 +342,7 @@ impl SqliteStore { } async fn load_inbound_group_sessions(&self) -> Result> { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + 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( @@ -357,7 +373,7 @@ impl SqliteStore { } async fn save_tracked_user(&self, user: &UserId, dirty: bool) -> Result<()> { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; query( @@ -378,7 +394,7 @@ impl SqliteStore { } async fn load_tracked_users(&self) -> Result<(HashSet, HashSet)> { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; let rows: Vec<(String, bool)> = query_as( @@ -410,7 +426,7 @@ impl SqliteStore { } async fn load_devices(&self) -> Result { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; let rows: Vec<(i64, String, String, Option, i64)> = query_as( @@ -490,7 +506,7 @@ impl SqliteStore { } async fn save_device_helper(&self, device: Device) -> Result<()> { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; @@ -573,20 +589,26 @@ impl CryptoStore for SqliteStore { WHERE user_id = ? and device_id = ?", ) .bind(self.user_id.as_str()) - .bind(&*self.device_id) + .bind((&*self.device_id).as_ref()) .fetch_optional(&mut *connection) .await?; let result = if let Some((id, pickle, shared, uploaded_key_count)) = row { - self.account_id = Some(id); - Some(Account::from_pickle( + let account = Account::from_pickle( pickle, self.get_pickle_mode(), shared, uploaded_key_count, &self.user_id, &self.device_id, - )?) + )?; + + self.account_info = Some(AccountInfo { + account_id: id, + identity_keys: account.identity_keys.clone(), + }); + + Some(account) } else { return Ok(None); }; @@ -640,19 +662,22 @@ impl CryptoStore for SqliteStore { .fetch_one(&mut *connection) .await?; - self.account_id = Some(account_id.0); + self.account_info = Some(AccountInfo { + account_id: account_id.0, + identity_keys: account.identity_keys.clone(), + }); Ok(()) } async fn save_sessions(&mut self, sessions: &[Session]) -> Result<()> { + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; + // TODO turn this into a transaction for session in sessions { self.lazy_load_sessions(&session.sender_key).await?; self.sessions.add(session.clone()).await; - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; - 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())?; @@ -683,7 +708,7 @@ impl CryptoStore for SqliteStore { } async fn save_inbound_group_session(&mut self, session: InboundGroupSession) -> Result { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let pickle = session.pickle(self.get_pickle_mode()).await; let mut connection = self.connection.lock().await; let session_id = session.session_id(); @@ -753,7 +778,7 @@ impl CryptoStore for SqliteStore { } async fn delete_device(&self, device: Device) -> Result<()> { - let account_id = self.account_id.ok_or(CryptoStoreError::AccountUnset)?; + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; let mut connection = self.connection.lock().await; query(