From 529d1e0117e24ec2f059d0c3afc04a782e314666 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Mon, 11 Apr 2022 13:52:08 +0200 Subject: [PATCH 01/32] removing store key from sled store - for values --- crates/matrix-sdk-sled/src/state_store.rs | 58 ++++++++--------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index ef8f8328c..4d780c91e 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -24,11 +24,11 @@ use anyhow::anyhow; use async_stream::stream; use futures_core::stream::Stream; use futures_util::stream::{self, TryStreamExt}; +use matrix_sdk_store_encryption::{StoreCipher, Error as KeyEncryptionError}; use matrix_sdk_base::{ deserialized_responses::{MemberEvent, SyncRoomEvent}, media::{MediaRequest, UniqueKey}, store::{ - store_key::{self, EncryptedEvent, StoreKey}, BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError, }, RoomInfo, @@ -65,18 +65,12 @@ use crate::encode_key::{encode_key_with_usize, EncodeKey, ENCODE_SEPARATOR}; #[cfg(feature = "crypto-store")] pub use crate::CryptoStore; -#[derive(Debug, Serialize, Deserialize)] -pub enum DatabaseType { - Unencrypted, - Encrypted(store_key::EncryptedStoreKey), -} - #[derive(Debug, thiserror::Error)] pub enum SledStoreError { #[error(transparent)] Json(#[from] serde_json::Error), #[error(transparent)] - Encryption(#[from] store_key::Error), + Encryption(#[from] KeyEncryptionError), #[error(transparent)] StoreError(#[from] StoreError), #[error(transparent)] @@ -103,9 +97,11 @@ impl Into for SledStoreError { SledStoreError::Json(e) => StoreError::Json(e), SledStoreError::Identifier(e) => StoreError::Identifier(e), SledStoreError::Encryption(e) => match e { - store_key::Error::Random(e) => StoreError::Encryption(e.to_string()), - store_key::Error::Serialization(e) => StoreError::Json(e), - store_key::Error::Encryption(e) => StoreError::Encryption(e), + KeyEncryptionError::Random(e) => StoreError::Encryption(e.to_string()), + KeyEncryptionError::Serialization(e) => StoreError::Json(e), + KeyEncryptionError::Encryption(e) => StoreError::Encryption(e.to_string()), + KeyEncryptionError::Version(found, expected) => StoreError::Encryption(format!("Bad Database Encryption Version: expected {} found {}", expected, found)), + KeyEncryptionError::Length(found, expected) => StoreError::Encryption(format!("The database key an invalid length: expected {} found {}", expected, found)), }, SledStoreError::StoreError(e) => e, _ => StoreError::Backend(anyhow!(self)), @@ -132,7 +128,7 @@ pub fn decode_key_value(key: &[u8], position: usize) -> Option { pub struct SledStore { path: Option, pub(crate) inner: Db, - store_key: Arc>, + store_cipher: Arc>, session: Tree, account_data: Tree, members: Tree, @@ -167,7 +163,7 @@ impl std::fmt::Debug for SledStore { } impl SledStore { - fn open_helper(db: Db, path: Option, store_key: Option) -> Result { + fn open_helper(db: Db, path: Option, store_cipher: Option) -> Result { let session = db.open_tree("session")?; let account_data = db.open_tree("account_data")?; @@ -200,7 +196,7 @@ impl SledStore { Ok(Self { path, inner: db, - store_key: store_key.into(), + store_cipher: store_cipher.into(), session, account_data, members, @@ -240,27 +236,15 @@ impl SledStore { let path = path.as_ref().join("matrix-sdk-state"); let db = Config::new().temporary(false).path(&path).open()?; - let store_key: Option = db - .get("store_key".encode())? - .map(|k| serde_json::from_slice(&k).map_err(StoreError::Json)) - .transpose()?; - - let store_key = if let Some(key) = store_key { - if let DatabaseType::Encrypted(k) = key { - StoreKey::import(passphrase, k).map_err(|_| StoreError::StoreLocked)? - } else { - return Err(SledStoreError::StoreError(StoreError::UnencryptedStore)); - } + let store_cipher = if let Some(inner) = db.get("store_cipher".encode())? { + StoreCipher::import(passphrase, &inner)? } else { - let key = StoreKey::new().map_err::(|e| e.into())?; - let encrypted_key = DatabaseType::Encrypted( - key.export(passphrase).map_err::(|e| e.into())?, - ); - db.insert("store_key".encode(), serde_json::to_vec(&encrypted_key)?)?; - key + let ciph = StoreCipher::new()?; + db.insert("store_cipher".encode(), ciph.export(passphrase)?); + ciph }; - SledStore::open_helper(db, Some(path), Some(store_key)) + SledStore::open_helper(db, Some(path), Some(store_cipher)) } pub fn open_with_path(path: impl AsRef) -> StoreResult { @@ -286,9 +270,8 @@ impl SledStore { } fn serialize_event(&self, event: &impl Serialize) -> Result, SledStoreError> { - if let Some(key) = &*self.store_key { - let encrypted = key.encrypt(event)?; - Ok(serde_json::to_vec(&encrypted)?) + if let Some(key) = &*self.store_cipher { + Ok(key.encrypt_value(event)?) } else { Ok(serde_json::to_vec(event)?) } @@ -298,9 +281,8 @@ impl SledStore { &self, event: &[u8], ) -> Result { - if let Some(key) = &*self.store_key { - let encrypted: EncryptedEvent = serde_json::from_slice(event)?; - Ok(key.decrypt(encrypted)?) + if let Some(key) = &*self.store_cipher { + Ok(key.decrypt_value(event)?) } else { Ok(serde_json::from_slice(event)?) } From f79e6b87e28d5ed63373cfd9cbfd7946d2b5cca2 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Mon, 11 Apr 2022 20:42:35 +0200 Subject: [PATCH 02/32] use result --- crates/matrix-sdk-sled/src/state_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 4d780c91e..88d02fcfe 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -240,7 +240,7 @@ impl SledStore { StoreCipher::import(passphrase, &inner)? } else { let ciph = StoreCipher::new()?; - db.insert("store_cipher".encode(), ciph.export(passphrase)?); + db.insert("store_cipher".encode(), ciph.export(passphrase)?)?; ciph }; From 918d95a67227d8b0c0a119ecc5b4ee03bd70fe1e Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Mon, 11 Apr 2022 22:11:14 +0200 Subject: [PATCH 03/32] first batch of data for testing encrypted sled store --- crates/matrix-sdk-sled/src/cryptostore.rs | 46 +---- crates/matrix-sdk-sled/src/encode_key.rs | 216 ++++++++++++++++++---- crates/matrix-sdk-sled/src/state_store.rs | 216 ++++++++++++++-------- 3 files changed, 325 insertions(+), 153 deletions(-) diff --git a/crates/matrix-sdk-sled/src/cryptostore.rs b/crates/matrix-sdk-sled/src/cryptostore.rs index d16324285..c8afd2fd8 100644 --- a/crates/matrix-sdk-sled/src/cryptostore.rs +++ b/crates/matrix-sdk-sled/src/cryptostore.rs @@ -50,7 +50,7 @@ use sled::{ use tracing::debug; use super::OpenStoreError; -use crate::encode_key::{EncodeKey, ENCODE_SEPARATOR}; +use crate::encode_key::{EncodeKey, EncodeSecureKey, ENCODE_SEPARATOR}; const DATABASE_VERSION: u8 = 4; @@ -156,41 +156,18 @@ impl EncodeSecureKey for RequestedKeyInfo { } } -impl EncodeSecureKey for UserId { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let user_id = store_cipher.hash_key(table_name, self.as_bytes()); - - [user_id.as_slice(), &[0xff]].concat() - } -} - impl EncodeSecureKey for ReadOnlyDevice { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { (self.user_id(), self.device_id()).encode_secure(table_name, store_cipher) } } -impl EncodeSecureKey for str { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let key = store_cipher.hash_key(table_name, self.as_bytes()); - [key.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - impl EncodeSecureKey for OutboundGroupSession { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { self.room_id().encode_secure(table_name, store_cipher) } } -impl EncodeSecureKey for RoomId { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let room_id = store_cipher.hash_key(table_name, self.as_bytes()); - - [room_id.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - impl EncodeSecureKey for InboundGroupSession { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { (self.room_id(), self.sender_key(), self.session_id()) @@ -198,24 +175,6 @@ impl EncodeSecureKey for InboundGroupSession { } } -impl EncodeSecureKey for (&RoomId, &str, &str) { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let first = store_cipher.hash_key(table_name, self.0.as_bytes()); - let second = store_cipher.hash_key(table_name, self.1.as_bytes()); - let third = store_cipher.hash_key(table_name, self.2.as_bytes()); - - [ - first.as_slice(), - &[ENCODE_SEPARATOR], - second.as_slice(), - &[ENCODE_SEPARATOR], - third.as_slice(), - &[ENCODE_SEPARATOR], - ] - .concat() - } -} - impl EncodeSecureKey for Session { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { let sender_key = @@ -227,9 +186,6 @@ impl EncodeSecureKey for Session { } } -trait EncodeSecureKey { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec; -} #[derive(Clone, Debug)] pub struct AccountInfo { diff --git a/crates/matrix-sdk-sled/src/encode_key.rs b/crates/matrix-sdk-sled/src/encode_key.rs index a57578a90..957b61399 100644 --- a/crates/matrix-sdk-sled/src/encode_key.rs +++ b/crates/matrix-sdk-sled/src/encode_key.rs @@ -1,7 +1,12 @@ use matrix_sdk_common::ruma::{ - events::{secret::request::SecretName, GlobalAccountDataEventType, StateEventType}, + events::{secret::request::SecretName, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType}, + receipt::ReceiptType, + EventEncryptionAlgorithm, DeviceId, EventId, MxcUri, RoomId, TransactionId, UserId, }; +use matrix_sdk_store_encryption::StoreCipher; + + pub const ENCODE_SEPARATOR: u8 = 0xff; pub trait EncodeKey { @@ -70,6 +75,26 @@ impl EncodeKey for SecretName { } } +impl EncodeKey for ReceiptType { + fn encode(&self) -> Vec { + let s: &str = self.as_ref(); + s.encode() + } +} + +impl EncodeKey for EventEncryptionAlgorithm { + fn encode(&self) -> Vec { + let s: &str = self.as_ref(); + s.encode() + } +} + +impl EncodeKey for RoomAccountDataEventType { + fn encode(&self) -> Vec { + self.to_string().encode() + } +} + impl EncodeKey for UserId { fn encode(&self) -> Vec { self.as_str().encode() @@ -78,15 +103,13 @@ impl EncodeKey for UserId { impl EncodeKey for (A, B) where - A: AsRef, - B: AsRef, + A: EncodeKey, + B: EncodeKey, { fn encode(&self) -> Vec { [ - self.0.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], - self.1.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], + self.0.encode(), + self.1.encode(), ] .concat() } @@ -94,18 +117,15 @@ where impl EncodeKey for (A, B, C) where - A: AsRef, - B: AsRef, - C: AsRef, + A: EncodeKey, + B: EncodeKey, + C: EncodeKey, { fn encode(&self) -> Vec { [ - self.0.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], - self.1.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], - self.2.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], + self.0.encode(), + self.1.encode(), + self.2.encode(), ] .concat() } @@ -113,21 +133,17 @@ where impl EncodeKey for (A, B, C, D) where - A: AsRef, - B: AsRef, - C: AsRef, - D: AsRef, + A: EncodeKey, + B: EncodeKey, + C: EncodeKey, + D: EncodeKey, { fn encode(&self) -> Vec { [ - self.0.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], - self.1.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], - self.2.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], - self.3.as_ref().as_bytes(), - &[ENCODE_SEPARATOR], + self.0.encode(), + self.1.encode(), + self.2.encode(), + self.3.encode(), ] .concat() } @@ -145,9 +161,143 @@ impl EncodeKey for GlobalAccountDataEventType { } } -#[cfg(feature = "state-store")] -pub fn encode_key_with_usize>(s: A, i: usize) -> Vec { - // FIXME: Not portable across architectures - [s.as_ref().as_bytes(), &[ENCODE_SEPARATOR], i.to_be_bytes().as_ref(), &[ENCODE_SEPARATOR]] - .concat() + +pub trait EncodeSecureKey { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec; } + + +impl EncodeSecureKey for &T { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + T::encode_secure(self, table_name, store_cipher) + } +} + +impl EncodeSecureKey for Box { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + T::encode_secure(self, table_name, store_cipher) + } +} + + +impl EncodeSecureKey for UserId { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let user_id = store_cipher.hash_key(table_name, self.as_bytes()); + + [user_id.as_slice(), &[ENCODE_SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for str { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let key = store_cipher.hash_key(table_name, self.as_bytes()); + [key.as_slice(), &[ENCODE_SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for String { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let key = store_cipher.hash_key(table_name, self.as_bytes()); + [key.as_slice(), &[ENCODE_SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for RoomId { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let room_id = store_cipher.hash_key(table_name, self.as_bytes()); + + [room_id.as_slice(), &[ENCODE_SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for EventId { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let event_id = store_cipher.hash_key(table_name, self.as_bytes()); + + [event_id.as_slice(), &[ENCODE_SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for ReceiptType { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let event_id = store_cipher.hash_key(table_name, self.as_str().as_bytes()); + + [event_id.as_slice(), &[ENCODE_SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for GlobalAccountDataEventType { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + ].concat() + } +} + +impl EncodeSecureKey for StateEventType { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + ].concat() + } +} + +impl EncodeSecureKey for RoomAccountDataEventType { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + ].concat() + } +} + + +impl EncodeSecureKey for (A, B) +where + A: EncodeSecureKey, + B: EncodeSecureKey, +{ + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + self.0.encode_secure(table_name, store_cipher), + self.1.encode_secure(table_name, store_cipher), + ] + .concat() + } +} + +impl EncodeSecureKey for (A, B, C) +where + A: EncodeSecureKey, + B: EncodeSecureKey, + C: EncodeSecureKey, +{ + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + self.0.encode_secure(table_name, store_cipher), + self.1.encode_secure(table_name, store_cipher), + self.2.encode_secure(table_name, store_cipher), + ] + .concat() + } +} + +impl EncodeSecureKey for (A, B, C, D) +where + A: EncodeSecureKey, + B: EncodeSecureKey, + C: EncodeSecureKey, + D: EncodeSecureKey, +{ + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + self.0.encode_secure(table_name, store_cipher), + self.1.encode_secure(table_name, store_cipher), + self.2.encode_secure(table_name, store_cipher), + self.3.encode_secure(table_name, store_cipher), + ] + .concat() + } +} \ No newline at end of file diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 88d02fcfe..5a59643e0 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -61,7 +61,7 @@ use tracing::{info, warn}; #[cfg(feature = "crypto-store")] use super::OpenStoreError; -use crate::encode_key::{encode_key_with_usize, EncodeKey, ENCODE_SEPARATOR}; +use crate::encode_key::{EncodeSecureKey, EncodeKey, ENCODE_SEPARATOR}; #[cfg(feature = "crypto-store")] pub use crate::CryptoStore; @@ -109,6 +109,32 @@ impl Into for SledStoreError { } } + +const SESSION: &str = "session"; +const ACCOUNT_DATA: &str = "account-data"; +const MEMBER: &str = "member"; +const PROFILE: &str = "profile"; +const ROOM_STATE: &str = "room-state"; +const PRESENCE: &str = "presence"; +const ROOM: &str = "room"; +const ROOM_INFO: &str = "room-info"; +const DISPLAY_NAME: &str = "display-name"; +const JOINED_USER_ID: &str = "joined-user-id"; +const INVITED_USER_ID: &str = "invited-user-ids"; +const ROOM_ACCOUNT_DATA: &str = "room-account-data"; +const STRIPPED_ROOM: &str = "stripped-room"; +const STRIPPED_ROOM_INFO: &str = "stripped-room-info"; +const STRIPPED_ROOM_MEMBER: &str = "stripped-room-member"; +const STRIPPED_ROOM_STATE: &str = "stripped-room-state"; +const ROOM_USER_RECEIPT: &str = "room-user-receipt"; +const ROOM_EVENT_RECEIPT: &str = "room-event-receipt"; +const MEDIA: &str = "media"; +const CUSTOM: &str = "custom"; +const TIMELINE: &str = "timeline"; +const TIMELINE_METADTA: &str = "timeline-metadata"; +const ROOM_EVENT_ID_POSITION: &str = "room-event-id-to-position"; + + type Result = std::result::Result; /// Get the value at `position` in encoded `key`. @@ -118,12 +144,20 @@ type Result = std::result::Result; /// wasn't encoded in a tuple, use `0`. /// /// Returns `None` if there is no key at `position`. -pub fn decode_key_value(key: &[u8], position: usize) -> Option { +fn decode_key_value(key: &[u8], position: usize) -> Option { let values: Vec<&[u8]> = key.split(|v| *v == ENCODE_SEPARATOR).collect(); values.get(position).map(|s| String::from_utf8_lossy(s).to_string()) } + +pub fn encode_key_with_usize>(s: A, i: usize) -> Vec { + // FIXME: Not portable across architectures + [s.as_ref().as_bytes(), &[ENCODE_SEPARATOR], i.to_be_bytes().as_ref(), &[ENCODE_SEPARATOR]] + .concat() +} + + #[derive(Clone)] pub struct SledStore { path: Option, @@ -164,34 +198,34 @@ impl std::fmt::Debug for SledStore { impl SledStore { fn open_helper(db: Db, path: Option, store_cipher: Option) -> Result { - let session = db.open_tree("session")?; - let account_data = db.open_tree("account_data")?; + let session = db.open_tree(SESSION)?; + let account_data = db.open_tree(ACCOUNT_DATA)?; - let members = db.open_tree("members")?; - let profiles = db.open_tree("profiles")?; - let display_names = db.open_tree("display_names")?; - let joined_user_ids = db.open_tree("joined_user_ids")?; - let invited_user_ids = db.open_tree("invited_user_ids")?; + let members = db.open_tree(MEMBER)?; + let profiles = db.open_tree(PROFILE)?; + let display_names = db.open_tree(DISPLAY_NAME)?; + let joined_user_ids = db.open_tree(JOINED_USER_ID)?; + let invited_user_ids = db.open_tree(INVITED_USER_ID)?; - let room_state = db.open_tree("room_state")?; - let room_info = db.open_tree("room_infos")?; - let presence = db.open_tree("presence")?; - let room_account_data = db.open_tree("room_account_data")?; + let room_state = db.open_tree(ROOM_STATE)?; + let room_info = db.open_tree(ROOM_INFO)?; + let presence = db.open_tree(PRESENCE)?; + let room_account_data = db.open_tree(ROOM_ACCOUNT_DATA)?; - let stripped_room_infos = db.open_tree("stripped_room_infos")?; - let stripped_members = db.open_tree("stripped_members")?; - let stripped_room_state = db.open_tree("stripped_room_state")?; + let stripped_room_infos = db.open_tree(STRIPPED_ROOM_INFO)?; + let stripped_members = db.open_tree(STRIPPED_ROOM_MEMBER)?; + let stripped_room_state = db.open_tree(STRIPPED_ROOM_STATE)?; - let room_user_receipts = db.open_tree("room_user_receipts")?; - let room_event_receipts = db.open_tree("room_event_receipts")?; + let room_user_receipts = db.open_tree(ROOM_USER_RECEIPT)?; + let room_event_receipts = db.open_tree(ROOM_EVENT_RECEIPT)?; - let media = db.open_tree("media")?; + let media = db.open_tree(MEDIA)?; - let custom = db.open_tree("custom")?; + let custom = db.open_tree(CUSTOM)?; - let room_timeline = db.open_tree("room_timeline")?; - let room_timeline_metadata = db.open_tree("room_timeline_metadata")?; - let room_event_id_to_position = db.open_tree("room_event_id_to_position")?; + let room_timeline = db.open_tree(TIMELINE)?; + let room_timeline_metadata = db.open_tree(TIMELINE_METADTA)?; + let room_event_id_to_position = db.open_tree(ROOM_EVENT_ID_POSITION)?; Ok(Self { path, @@ -228,6 +262,16 @@ impl SledStore { SledStore::open_helper(db, None, None).map_err(|e| e.into()) } + // testing only + #[cfg(test)] + fn open_encrypted() -> StoreResult { + let db = + Config::new().temporary(true).open().map_err(|e| StoreError::Backend(anyhow!(e)))?; + + SledStore::open_helper(db, None, Some(StoreCipher::new().expect("can't create store cipher"))).map_err(|e| e.into()) + } + + pub fn open_with_passphrase(path: impl AsRef, passphrase: &str) -> StoreResult { Self::inner_open_with_passphrase(path, passphrase).map_err(|e| e.into()) } @@ -287,9 +331,21 @@ impl SledStore { Ok(serde_json::from_slice(event)?) } } + + fn encode_key( + &self, + table_name: &str, + key: &T, + ) -> Vec { + if let Some(store_cipher) = &*self.store_cipher { + key.encode_secure(table_name, store_cipher).to_vec() + } else { + key.encode() + } + } pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { - self.session.insert(("filter", filter_name).encode(), filter_id)?; + self.session.insert(self.encode_key(SESSION, &("filter", filter_name)), filter_id)?; Ok(()) } @@ -297,7 +353,7 @@ impl SledStore { pub async fn get_filter(&self, filter_name: &str) -> Result> { Ok(self .session - .get(("filter", filter_name).encode())? + .get(self.encode_key(SESSION, &("filter", filter_name)))? .map(|f| String::from_utf8_lossy(&f).to_string())) } @@ -352,25 +408,25 @@ impl SledStore { let profile_changes = changes.profiles.get(room); for event in events.values() { - let key = (room, &event.state_key).encode(); + let key = (room, &event.state_key); match event.content.membership { MembershipState::Join => { - joined.insert(key.as_slice(), event.state_key.as_str())?; - invited.remove(key.as_slice())?; + joined.insert(self.encode_key(JOINED_USER_ID, &key), event.state_key.as_str())?; + invited.remove(self.encode_key(INVITED_USER_ID, &key))?; } MembershipState::Invite => { - invited.insert(key.as_slice(), event.state_key.as_str())?; - joined.remove(key.as_slice())?; + invited.insert(self.encode_key(INVITED_USER_ID, &key), event.state_key.as_str())?; + joined.remove(self.encode_key(JOINED_USER_ID, &key))?; } _ => { - joined.remove(key.as_slice())?; - invited.remove(key.as_slice())?; + joined.remove(self.encode_key(JOINED_USER_ID, &key))?; + invited.remove(self.encode_key(INVITED_USER_ID, &key))?; } } members.insert( - key.as_slice(), + self.encode_key(MEMBER, &key), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -379,7 +435,7 @@ impl SledStore { profile_changes.and_then(|p| p.get(&event.state_key)) { profiles.insert( - key.as_slice(), + self.encode_key(PROFILE, &key), self.serialize_event(&profile) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -390,7 +446,7 @@ impl SledStore { for (room_id, ambiguity_maps) in &changes.ambiguity_maps { for (display_name, map) in ambiguity_maps { display_names.insert( - (room_id, display_name).encode(), + self.encode_key(DISPLAY_NAME, &(room_id, display_name)), self.serialize_event(&map) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -399,7 +455,7 @@ impl SledStore { for (event_type, event) in &changes.account_data { account_data.insert( - event_type.encode(), + self.encode_key(ACCOUNT_DATA, event_type), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -408,7 +464,7 @@ impl SledStore { for (room, events) in &changes.room_account_data { for (event_type, event) in events { room_account_data.insert( - (room, event_type.to_string()).encode(), + self.encode_key(ROOM_ACCOUNT_DATA, &(room, event_type)), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -419,7 +475,7 @@ impl SledStore { for (event_type, events) in event_types { for (state_key, event) in events { state.insert( - (room, event_type.to_string(), state_key).encode(), + self.encode_key(ROOM_STATE, &(room, event_type, state_key)), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -429,7 +485,7 @@ impl SledStore { for (room_id, room_info) in &changes.room_infos { rooms.insert( - room_id.encode(), + self.encode_key(ROOM, room_id), self.serialize_event(room_info) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -437,7 +493,7 @@ impl SledStore { for (sender, event) in &changes.presence { presence.insert( - sender.encode(), + self.encode_key(PRESENCE, sender), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -445,7 +501,7 @@ impl SledStore { for (room_id, info) in &changes.stripped_room_infos { striped_rooms.insert( - room_id.encode(), + self.encode_key(STRIPPED_ROOM_INFO, room_id), self.serialize_event(&info) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -454,7 +510,7 @@ impl SledStore { for (room, events) in &changes.stripped_members { for event in events.values() { stripped_members.insert( - (room, event.state_key.to_string()).encode(), + self.encode_key(STRIPPED_ROOM_MEMBER, &(room, event.state_key.to_string())), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -465,7 +521,7 @@ impl SledStore { for (event_type, events) in event_types { for (state_key, event) in events { stripped_state.insert( - (room, event_type.to_string(), state_key).encode(), + self.encode_key(STRIPPED_ROOM_STATE, &(room, event_type.to_string(), state_key)), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -488,7 +544,7 @@ impl SledStore { for (user_id, receipt) in receipts { // Add the receipt to the room user receipts if let Some(old) = room_user_receipts.insert( - (room, receipt_type, user_id).encode(), + self.encode_key(ROOM_USER_RECEIPT, &(room, receipt_type, user_id)), self.serialize_event(&(event_id, receipt)) .map_err(ConflictableTransactionError::Abort)?, )? { @@ -497,13 +553,13 @@ impl SledStore { .deserialize_event(&old) .map_err(ConflictableTransactionError::Abort)?; room_event_receipts.remove( - (room, receipt_type, old_event, user_id).encode(), + self.encode_key(ROOM_EVENT_RECEIPT, &(room, receipt_type, old_event, user_id)), )?; } // Add the receipt to the room event receipts room_event_receipts.insert( - (room, receipt_type, event_id, user_id).encode(), + self.encode_key(ROOM_EVENT_RECEIPT, &(room, receipt_type, event_id, user_id)), self.serialize_event(receipt) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -529,7 +585,7 @@ impl SledStore { pub async fn get_presence_event(&self, user_id: &UserId) -> Result>> { let db = self.clone(); - let key = user_id.encode(); + let key = self.encode_key(PRESENCE, user_id); spawn_blocking(move || db.presence.get(key)?.map(|e| db.deserialize_event(&e)).transpose()) .await? } @@ -541,7 +597,7 @@ impl SledStore { state_key: &str, ) -> Result>> { let db = self.clone(); - let key = (room_id, event_type.to_string(), state_key).encode(); + let key = self.encode_key(ROOM_STATE, &(room_id, event_type.to_string(), state_key)); spawn_blocking(move || { db.room_state.get(key)?.map(|e| db.deserialize_event(&e)).transpose() }) @@ -554,7 +610,7 @@ impl SledStore { event_type: StateEventType, ) -> Result>> { let db = self.clone(); - let key = (room_id, event_type.to_string()).encode(); + let key = self.encode_key(ROOM_STATE, &(room_id, event_type.to_string())); spawn_blocking(move || { db.room_state .scan_prefix(key) @@ -570,7 +626,7 @@ impl SledStore { user_id: &UserId, ) -> Result> { let db = self.clone(); - let key = (room_id, user_id).encode(); + let key = self.encode_key(PROFILE, &(room_id, user_id)); spawn_blocking(move || db.profiles.get(key)?.map(|p| db.deserialize_event(&p)).transpose()) .await? } @@ -581,7 +637,7 @@ impl SledStore { state_key: &UserId, ) -> Result> { let db = self.clone(); - let key = (room_id, state_key).encode(); + let key = self.encode_key(MEMBER, &(room_id, state_key)); spawn_blocking(move || db.members.get(key)?.map(|v| db.deserialize_event(&v)).transpose()) .await? } @@ -808,60 +864,59 @@ impl SledStore { } async fn remove_room(&self, room_id: &RoomId) -> Result<()> { - let room_key = room_id.encode(); - let mut members_batch = sled::Batch::default(); - for key in self.members.scan_prefix(room_key.as_slice()).keys() { + let mut members_batch = sled::Batch::default(); + for key in self.members.scan_prefix(self.encode_key(MEMBER, room_id)).keys() { members_batch.remove(key?) } let mut profiles_batch = sled::Batch::default(); - for key in self.profiles.scan_prefix(room_key.as_slice()).keys() { + for key in self.profiles.scan_prefix(self.encode_key(PROFILE, room_id)).keys() { profiles_batch.remove(key?) } let mut display_names_batch = sled::Batch::default(); - for key in self.display_names.scan_prefix(room_key.as_slice()).keys() { + for key in self.display_names.scan_prefix(self.encode_key(DISPLAY_NAME, room_id)).keys() { display_names_batch.remove(key?) } let mut joined_user_ids_batch = sled::Batch::default(); - for key in self.joined_user_ids.scan_prefix(room_key.as_slice()).keys() { + for key in self.joined_user_ids.scan_prefix(self.encode_key(JOINED_USER_ID, room_id)).keys() { joined_user_ids_batch.remove(key?) } let mut invited_user_ids_batch = sled::Batch::default(); - for key in self.invited_user_ids.scan_prefix(room_key.as_slice()).keys() { + for key in self.invited_user_ids.scan_prefix(self.encode_key(INVITED_USER_ID, room_id)).keys() { invited_user_ids_batch.remove(key?) } let mut room_state_batch = sled::Batch::default(); - for key in self.room_state.scan_prefix(room_key.as_slice()).keys() { + for key in self.room_state.scan_prefix(self.encode_key(ROOM_STATE, room_id)).keys() { room_state_batch.remove(key?) } let mut room_account_data_batch = sled::Batch::default(); - for key in self.room_account_data.scan_prefix(room_key.as_slice()).keys() { + for key in self.room_account_data.scan_prefix(self.encode_key(ROOM_ACCOUNT_DATA, room_id)).keys() { room_account_data_batch.remove(key?) } let mut stripped_members_batch = sled::Batch::default(); - for key in self.stripped_members.scan_prefix(room_key.as_slice()).keys() { + for key in self.stripped_members.scan_prefix(self.encode_key(STRIPPED_ROOM_MEMBER, room_id)).keys() { stripped_members_batch.remove(key?) } let mut stripped_room_state_batch = sled::Batch::default(); - for key in self.stripped_room_state.scan_prefix(room_key.as_slice()).keys() { + for key in self.stripped_room_state.scan_prefix(self.encode_key(STRIPPED_ROOM, room_id)).keys() { stripped_room_state_batch.remove(key?) } let mut room_user_receipts_batch = sled::Batch::default(); - for key in self.room_user_receipts.scan_prefix(room_key.as_slice()).keys() { + for key in self.room_user_receipts.scan_prefix(self.encode_key(ROOM_USER_RECEIPT, room_id)).keys() { room_user_receipts_batch.remove(key?) } let mut room_event_receipts_batch = sled::Batch::default(); - for key in self.room_event_receipts.scan_prefix(room_key.as_slice()).keys() { + for key in self.room_event_receipts.scan_prefix(self.encode_key(ROOM_EVENT_RECEIPT, room_id)).keys() { room_event_receipts_batch.remove(key?) } @@ -896,8 +951,8 @@ impl SledStore { room_user_receipts, room_event_receipts, )| { - rooms.remove(room_key.as_slice())?; - stripped_rooms.remove(room_key.as_slice())?; + rooms.remove(self.encode_key(ROOM, room_id))?; + stripped_rooms.remove(self.encode_key(STRIPPED_ROOM, room_id))?; members.apply_batch(&members_batch)?; profiles.apply_batch(&profiles_batch)?; @@ -929,7 +984,7 @@ impl SledStore { room_id: &RoomId, ) -> Result>, Option)>> { let db = self.clone(); - let key = room_id.encode(); + let key = self.encode_key("ROOM_TIMELINE_METADATA", room_id); let r_id = room_id.to_owned(); let metadata: Option = db .room_timeline_metadata @@ -960,16 +1015,15 @@ impl SledStore { } async fn remove_room_timeline(&self, room_id: &RoomId) -> Result<()> { - let room_key = room_id.encode(); info!("Remove stored timeline for {}", room_id); let mut timeline_batch = sled::Batch::default(); - for key in self.room_timeline.scan_prefix(room_key.as_slice()).keys() { + for key in self.room_timeline.scan_prefix(self.encode_key("ROOM_TIMELINE", &room_id)).keys() { timeline_batch.remove(key?) } let mut event_id_to_position_batch = sled::Batch::default(); - for key in self.room_event_id_to_position.scan_prefix(room_key.as_slice()).keys() { + for key in self.room_event_id_to_position.scan_prefix(self.encode_key(ROOM, &room_id)).keys() { event_id_to_position_batch.remove(key?) } @@ -977,7 +1031,7 @@ impl SledStore { (&self.room_timeline, &self.room_timeline_metadata, &self.room_event_id_to_position) .transaction( |(room_timeline, room_timeline_metadata, room_event_id_to_position)| { - room_timeline_metadata.remove(room_key.as_slice())?; + room_timeline_metadata.remove(self.encode_key("ROOM_TIMELINE_METADATA", &room_id))?; room_timeline.apply_batch(&timeline_batch)?; room_event_id_to_position.apply_batch(&event_id_to_position_batch)?; @@ -1002,7 +1056,6 @@ impl SledStore { } else { info!("Save new timeline batch from messages response for {}", room_id); } - let room_key = room_id.encode(); let metadata: Option = if timeline.limited { info!( @@ -1014,7 +1067,7 @@ impl SledStore { } else { let metadata: Option = self .room_timeline_metadata - .get(room_key.as_slice())? + .get(self.encode_key("ROOM_TIMELINE_METADATA", &room_id))? .map(|v| serde_json::from_slice(&v).map_err(StoreError::Json)) .transpose()?; if let Some(mut metadata) = metadata { @@ -1030,7 +1083,7 @@ impl SledStore { let mut delete_timeline = false; for event in &timeline.events { if let Some(event_id) = event.event_id() { - let event_key = (room_id, event_id).encode(); + let event_key = self.encode_key("ROOM_EVENT_ID_POSITION", &(room_id, event_id)); if self.room_event_id_to_position.contains_key(event_key)? { delete_timeline = true; break; @@ -1131,7 +1184,7 @@ impl SledStore { } } - timeline_metadata_batch.insert(room_key, serde_json::to_vec(&metadata)?); + timeline_metadata_batch.insert(self.encode_key(TIMELINE_METADTA, &room_id), serde_json::to_vec(&metadata)?); } let ret: Result<(), TransactionError> = @@ -1340,3 +1393,16 @@ mod tests { statestore_integration_tests! { integration } } + +#[cfg(test)] +mod encrypted_tests { + use matrix_sdk_base::statestore_integration_tests; + + use super::{SledStore, StateStore, StoreResult}; + + async fn get_store() -> StoreResult { + SledStore::open_encrypted().map_err(Into::into) + } + + statestore_integration_tests! { integration } +} From 551711261d1b1fec15b98cd46626f7d3c4d05284 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Mon, 11 Apr 2022 22:49:09 +0200 Subject: [PATCH 04/32] integration tests for encrypted version --- .../src/store/integration_tests.rs | 36 ++++++++-------- crates/matrix-sdk-sled/src/encode_key.rs | 10 +++++ crates/matrix-sdk-sled/src/state_store.rs | 43 +++++++++---------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index 784adef54..a212752cd 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -285,10 +285,10 @@ macro_rules! statestore_integration_tests { assert_eq!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(), 1); assert!(store.get_profile(room_id, user_id).await?.is_some()); assert!(store.get_member_event(room_id, user_id).await?.is_some()); - assert_eq!(store.get_user_ids(room_id).await?.len(), 2); + assert_eq!(store.get_user_ids(room_id).await?.len(), 2, "expected to find 2 members for room"); assert_eq!(store.get_invited_user_ids(room_id).await?.len(), 1); assert_eq!(store.get_joined_user_ids(room_id).await?.len(), 1); - assert_eq!(store.get_users_with_display_name(room_id, "example").await?.len(), 2); + assert_eq!(store.get_users_with_display_name(room_id, "example").await?.len(), 2, "expected to find 2 display names for room"); assert!(store .get_room_account_data_event(room_id, RoomAccountDataEventType::Tag) .await? @@ -325,7 +325,7 @@ macro_rules! statestore_integration_tests { assert!(store.get_member_event(room_id, user_id).await.unwrap().is_some()); let members = store.get_user_ids(room_id).await.unwrap(); - assert!(!members.is_empty()) + assert!(!members.is_empty(), "We expected to find members for the room") } #[async_test] @@ -425,22 +425,22 @@ macro_rules! statestore_integration_tests { let mut changes = StateChanges::default(); changes.add_receipts(room_id, second_receipt_event); - store.save_changes(&changes).await.unwrap(); + store.save_changes(&changes).await.expect("Saving works"); assert!(store .get_user_room_receipt_event(room_id, ReceiptType::Read, user_id()) .await - .unwrap() + .expect("Getting user room receipts failed") .is_some()); assert!(store .get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id) .await - .unwrap() + .expect("Getting event room receipt events for first event failed") .is_empty()); assert_eq!( store .get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id) .await - .unwrap() + .expect("Getting event room receipt events for second event failed") .len(), 1 ); @@ -526,17 +526,17 @@ macro_rules! statestore_integration_tests { store.remove_room(room_id).await?; - assert_eq!(store.get_room_infos().await?.len(), 0); + assert!(store.get_room_infos().await?.is_empty(), "room is still there"); assert_eq!(store.get_stripped_room_infos().await?.len(), 1); assert!(store.get_state_event(room_id, StateEventType::RoomName, "").await?.is_none()); - assert_eq!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(), 0); + assert!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.is_empty(), "still state events found"); assert!(store.get_profile(room_id, user_id).await?.is_none()); assert!(store.get_member_event(room_id, user_id).await?.is_none()); - assert_eq!(store.get_user_ids(room_id).await?.len(), 0); - assert_eq!(store.get_invited_user_ids(room_id).await?.len(), 0); - assert_eq!(store.get_joined_user_ids(room_id).await?.len(), 0); - assert_eq!(store.get_users_with_display_name(room_id, "example").await?.len(), 0); + assert!(store.get_user_ids(room_id).await?.is_empty(), "still user ids found"); + assert!(store.get_invited_user_ids(room_id).await?.is_empty(), "still invited user ids found"); + assert!(store.get_joined_user_ids(room_id).await?.is_empty(), "still joined users found"); + assert!(store.get_users_with_display_name(room_id, "example").await?.is_empty(), "still display names found"); assert!(store .get_room_account_data_event(room_id, RoomAccountDataEventType::Tag) .await? @@ -545,18 +545,18 @@ macro_rules! statestore_integration_tests { .get_user_room_receipt_event(room_id, ReceiptType::Read, user_id) .await? .is_none()); - assert_eq!( + assert!( store .get_event_room_receipt_events(room_id, ReceiptType::Read, first_receipt_event_id()) .await? - .len(), - 0 + .is_empty(), + "still event recepts in the store" ); store.remove_room(stripped_room_id).await?; - assert_eq!(store.get_room_infos().await?.len(), 0); - assert_eq!(store.get_stripped_room_infos().await?.len(), 0); + assert!(store.get_room_infos().await?.is_empty(), "still room info found"); + assert!(store.get_stripped_room_infos().await?.is_empty(), "still stripped room info found"); Ok(()) } diff --git a/crates/matrix-sdk-sled/src/encode_key.rs b/crates/matrix-sdk-sled/src/encode_key.rs index 957b61399..097c4dc0b 100644 --- a/crates/matrix-sdk-sled/src/encode_key.rs +++ b/crates/matrix-sdk-sled/src/encode_key.rs @@ -218,6 +218,16 @@ impl EncodeSecureKey for EventId { } } +impl EncodeSecureKey for MxcUri { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let s: &str = self.as_ref(); + [ + store_cipher.hash_key(table_name, s.as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + ].concat() + } +} + impl EncodeSecureKey for ReceiptType { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { let event_id = store_cipher.hash_key(table_name, self.as_str().as_bytes()); diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 5a59643e0..acd8585c9 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -122,7 +122,6 @@ const DISPLAY_NAME: &str = "display-name"; const JOINED_USER_ID: &str = "joined-user-id"; const INVITED_USER_ID: &str = "invited-user-ids"; const ROOM_ACCOUNT_DATA: &str = "room-account-data"; -const STRIPPED_ROOM: &str = "stripped-room"; const STRIPPED_ROOM_INFO: &str = "stripped-room-info"; const STRIPPED_ROOM_MEMBER: &str = "stripped-room-member"; const STRIPPED_ROOM_STATE: &str = "stripped-room-state"; @@ -131,7 +130,7 @@ const ROOM_EVENT_RECEIPT: &str = "room-event-receipt"; const MEDIA: &str = "media"; const CUSTOM: &str = "custom"; const TIMELINE: &str = "timeline"; -const TIMELINE_METADTA: &str = "timeline-metadata"; +const TIMELINE_METADATA: &str = "timeline-metadata"; const ROOM_EVENT_ID_POSITION: &str = "room-event-id-to-position"; @@ -224,7 +223,7 @@ impl SledStore { let custom = db.open_tree(CUSTOM)?; let room_timeline = db.open_tree(TIMELINE)?; - let room_timeline_metadata = db.open_tree(TIMELINE_METADTA)?; + let room_timeline_metadata = db.open_tree(TIMELINE_METADATA)?; let room_event_id_to_position = db.open_tree(ROOM_EVENT_ID_POSITION)?; Ok(Self { @@ -739,7 +738,7 @@ impl SledStore { display_name: &str, ) -> Result>> { let db = self.clone(); - let key = (room_id, display_name).encode(); + let key = self.encode_key(DISPLAY_NAME, &(room_id, display_name)); spawn_blocking(move || { Ok(db .display_names @@ -756,7 +755,7 @@ impl SledStore { event_type: GlobalAccountDataEventType, ) -> Result>> { let db = self.clone(); - let key = event_type.encode(); + let key = self.encode_key(ACCOUNT_DATA, &event_type); spawn_blocking(move || { db.account_data.get(key)?.map(|m| db.deserialize_event(&m)).transpose() }) @@ -769,7 +768,7 @@ impl SledStore { event_type: RoomAccountDataEventType, ) -> Result>> { let db = self.clone(); - let key = (room_id, event_type.to_string()).encode(); + let key = self.encode_key(ROOM_ACCOUNT_DATA, &(room_id, event_type)); spawn_blocking(move || { db.room_account_data.get(key)?.map(|m| db.deserialize_event(&m)).transpose() }) @@ -783,7 +782,7 @@ impl SledStore { user_id: &UserId, ) -> Result, Receipt)>> { let db = self.clone(); - let key = (room_id, receipt_type, user_id).encode(); + let key = self.encode_key(ROOM_USER_RECEIPT, &(room_id, receipt_type, user_id)); spawn_blocking(move || { db.room_user_receipts.get(key)?.map(|m| db.deserialize_event(&m)).transpose() }) @@ -797,7 +796,7 @@ impl SledStore { event_id: &EventId, ) -> StoreResult, Receipt)>> { let db = self.clone(); - let key = (room_id, receipt_type, event_id).encode(); + let key = self.encode_key(ROOM_EVENT_RECEIPT, &(room_id, receipt_type, event_id)); spawn_blocking(move || { db.room_event_receipts .scan_prefix(key) @@ -819,7 +818,7 @@ impl SledStore { async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { self.media - .insert((request.source.unique_key(), request.format.unique_key()).encode(), data)?; + .insert(self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())), data)?; self.inner.flush_async().await?; @@ -828,7 +827,7 @@ impl SledStore { async fn get_media_content(&self, request: &MediaRequest) -> Result>> { let db = self.clone(); - let key = (request.source.unique_key(), request.format.unique_key()).encode(); + let key = self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())); spawn_blocking(move || Ok(db.media.get(key)?.map(|m| m.to_vec()))).await? } @@ -847,13 +846,13 @@ impl SledStore { } async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> { - self.media.remove((request.source.unique_key(), request.format.unique_key()).encode())?; + self.media.remove(self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())))?; Ok(()) } async fn remove_media_content_for_uri(&self, uri: &MxcUri) -> Result<()> { - let keys = self.media.scan_prefix(uri.encode()).keys(); + let keys = self.media.scan_prefix(self.encode_key(MEDIA, uri)).keys(); let mut batch = sled::Batch::default(); for key in keys { @@ -906,7 +905,7 @@ impl SledStore { } let mut stripped_room_state_batch = sled::Batch::default(); - for key in self.stripped_room_state.scan_prefix(self.encode_key(STRIPPED_ROOM, room_id)).keys() { + for key in self.stripped_room_state.scan_prefix(self.encode_key(STRIPPED_ROOM_STATE, room_id)).keys() { stripped_room_state_batch.remove(key?) } @@ -952,7 +951,7 @@ impl SledStore { room_event_receipts, )| { rooms.remove(self.encode_key(ROOM, room_id))?; - stripped_rooms.remove(self.encode_key(STRIPPED_ROOM, room_id))?; + stripped_rooms.remove(self.encode_key(STRIPPED_ROOM_INFO, room_id))?; members.apply_batch(&members_batch)?; profiles.apply_batch(&profiles_batch)?; @@ -984,7 +983,7 @@ impl SledStore { room_id: &RoomId, ) -> Result>, Option)>> { let db = self.clone(); - let key = self.encode_key("ROOM_TIMELINE_METADATA", room_id); + let key = self.encode_key(TIMELINE_METADATA, room_id); let r_id = room_id.to_owned(); let metadata: Option = db .room_timeline_metadata @@ -1018,12 +1017,12 @@ impl SledStore { info!("Remove stored timeline for {}", room_id); let mut timeline_batch = sled::Batch::default(); - for key in self.room_timeline.scan_prefix(self.encode_key("ROOM_TIMELINE", &room_id)).keys() { + for key in self.room_timeline.scan_prefix(self.encode_key(TIMELINE, &room_id)).keys() { timeline_batch.remove(key?) } let mut event_id_to_position_batch = sled::Batch::default(); - for key in self.room_event_id_to_position.scan_prefix(self.encode_key(ROOM, &room_id)).keys() { + for key in self.room_event_id_to_position.scan_prefix(self.encode_key(ROOM_EVENT_ID_POSITION, &room_id)).keys() { event_id_to_position_batch.remove(key?) } @@ -1031,7 +1030,7 @@ impl SledStore { (&self.room_timeline, &self.room_timeline_metadata, &self.room_event_id_to_position) .transaction( |(room_timeline, room_timeline_metadata, room_event_id_to_position)| { - room_timeline_metadata.remove(self.encode_key("ROOM_TIMELINE_METADATA", &room_id))?; + room_timeline_metadata.remove(self.encode_key(TIMELINE_METADATA, &room_id))?; room_timeline.apply_batch(&timeline_batch)?; room_event_id_to_position.apply_batch(&event_id_to_position_batch)?; @@ -1067,7 +1066,7 @@ impl SledStore { } else { let metadata: Option = self .room_timeline_metadata - .get(self.encode_key("ROOM_TIMELINE_METADATA", &room_id))? + .get(self.encode_key(TIMELINE_METADATA, &room_id))? .map(|v| serde_json::from_slice(&v).map_err(StoreError::Json)) .transpose()?; if let Some(mut metadata) = metadata { @@ -1083,7 +1082,7 @@ impl SledStore { let mut delete_timeline = false; for event in &timeline.events { if let Some(event_id) = event.event_id() { - let event_key = self.encode_key("ROOM_EVENT_ID_POSITION", &(room_id, event_id)); + let event_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); if self.room_event_id_to_position.contains_key(event_key)? { delete_timeline = true; break; @@ -1138,7 +1137,7 @@ impl SledStore { AnySyncMessageLikeEvent::RoomRedaction(redaction), )) = event.event.deserialize() { - let redacts_key = (room_id, redaction.redacts).encode(); + let redacts_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, redaction.redacts)); if let Some(position_key) = self.room_event_id_to_position.get(redacts_key)? { @@ -1184,7 +1183,7 @@ impl SledStore { } } - timeline_metadata_batch.insert(self.encode_key(TIMELINE_METADTA, &room_id), serde_json::to_vec(&metadata)?); + timeline_metadata_batch.insert(self.encode_key(TIMELINE_METADATA, &room_id), serde_json::to_vec(&metadata)?); } let ret: Result<(), TransactionError> = From 000be73e80c022e0d6d742f29e76057500840955 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Mon, 11 Apr 2022 23:47:20 +0200 Subject: [PATCH 05/32] making tests more explicit --- .../src/store/integration_tests.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index a212752cd..8c80bad3b 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -354,7 +354,7 @@ macro_rules! statestore_integration_tests { #[async_test] async fn test_receipts_saving() { - let store = get_store().await.unwrap(); + let store = get_store().await.expect("creating store failed"); let room_id = room_id!("!test_receipts_saving:localhost"); @@ -370,7 +370,7 @@ macro_rules! statestore_integration_tests { } } })) - .unwrap(); + .expect("json creation failed"); let second_receipt_event = serde_json::from_value(json!({ second_event_id.clone(): { @@ -381,45 +381,45 @@ macro_rules! statestore_integration_tests { } } })) - .unwrap(); + .expect("json creation failed"); assert!(store .get_user_room_receipt_event(room_id, ReceiptType::Read, user_id()) .await - .unwrap() + .expect("failed to read user room receipt") .is_none()); assert!(store .get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id) .await - .unwrap() + .expect("failed to read user room receipt for 1") .is_empty()); assert!(store .get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id) .await - .unwrap() + .expect("failed to read user room receipt for 2") .is_empty()); let mut changes = StateChanges::default(); changes.add_receipts(room_id, first_receipt_event); - store.save_changes(&changes).await.unwrap(); + store.save_changes(&changes).await.expect("writing changes fauked"); assert!(store .get_user_room_receipt_event(room_id, ReceiptType::Read, user_id()) .await - .unwrap() + .expect("failed to read user room receipt after save") .is_some(),); assert_eq!( store .get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id) .await - .unwrap() + .expect("failed to read user room receipt for 1 after save") .len(), 1 ); assert!(store .get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id) .await - .unwrap() + .expect("failed to read user room receipt for 2 after save") .is_empty()); let mut changes = StateChanges::default(); From 88be0ee9c18dc1a7e550ce0d704c0c6de127c8dd Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 12:24:11 +0200 Subject: [PATCH 06/32] fix user id streams --- crates/matrix-sdk-sled/src/state_store.rs | 30 +++-------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index acd8585c9..58a367d6c 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -23,7 +23,7 @@ use std::{ use anyhow::anyhow; use async_stream::stream; use futures_core::stream::Stream; -use futures_util::stream::{self, TryStreamExt}; +use futures_util::stream::{self, StreamExt, TryStreamExt}; use matrix_sdk_store_encryption::{StoreCipher, Error as KeyEncryptionError}; use matrix_sdk_base::{ deserialized_responses::{MemberEvent, SyncRoomEvent}, @@ -645,29 +645,7 @@ impl SledStore { &self, room_id: &RoomId, ) -> StoreResult>>> { - let decode = |key: &[u8]| -> StoreResult> { - let mut iter = key.split(|c| c == &ENCODE_SEPARATOR); - // Our key is a the room id separated from the user id by a null - // byte, discard the first value of the split. - iter.next(); - - let user_id = iter.next().expect("User ids weren't properly encoded"); - - Ok(UserId::parse(String::from_utf8_lossy(user_id).to_string())?) - }; - - let members = self.members.clone(); - let key = room_id.encode(); - - spawn_blocking(move || { - stream::iter( - members - .scan_prefix(key) - .map(move |u| decode(&u.map_err(|e| StoreError::Backend(anyhow!(e)))?.0)), - ) - }) - .await - .map_err(|e| StoreError::Backend(anyhow!(e))) + Ok(self.get_joined_user_ids(room_id).await?.chain(self.get_invited_user_ids(room_id).await?)) } pub async fn get_invited_user_ids( @@ -675,7 +653,7 @@ impl SledStore { room_id: &RoomId, ) -> StoreResult>>> { let db = self.clone(); - let key = room_id.encode(); + let key = self.encode_key(INVITED_USER_ID, room_id); spawn_blocking(move || { stream::iter(db.invited_user_ids.scan_prefix(key).map(|u| { UserId::parse( @@ -694,7 +672,7 @@ impl SledStore { room_id: &RoomId, ) -> StoreResult>>> { let db = self.clone(); - let key = room_id.encode(); + let key = self.encode_key(JOINED_USER_ID, room_id); spawn_blocking(move || { stream::iter(db.joined_user_ids.scan_prefix(key).map(|u| { UserId::parse( From fb8f12361620e2010b7247c8ad62272a38a3a7d2 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 12:44:34 +0200 Subject: [PATCH 07/32] fixing remaining key lookup --- .../src/store/integration_tests.rs | 21 ++++++------- crates/matrix-sdk-sled/src/state_store.rs | 30 +++---------------- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index 8c80bad3b..8e414fb18 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -277,17 +277,17 @@ macro_rules! statestore_integration_tests { assert!(store.get_sync_token().await?.is_some()); assert!(store.get_presence_event(user_id).await?.is_some()); - assert_eq!(store.get_room_infos().await?.len(), 1); - assert_eq!(store.get_stripped_room_infos().await?.len(), 1); + assert_eq!(store.get_room_infos().await?.len(), 1, "Exepcted to find 1 room info "); + assert_eq!(store.get_stripped_room_infos().await?.len(), 1, "Exepcted to find 1 stripped room info"); assert!(store.get_account_data_event(GlobalAccountDataEventType::PushRules).await?.is_some()); assert!(store.get_state_event(room_id, StateEventType::RoomName, "").await?.is_some()); - assert_eq!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(), 1); + assert_eq!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(), 1, "Exepcted to find 1 room topic"); assert!(store.get_profile(room_id, user_id).await?.is_some()); assert!(store.get_member_event(room_id, user_id).await?.is_some()); assert_eq!(store.get_user_ids(room_id).await?.len(), 2, "expected to find 2 members for room"); - assert_eq!(store.get_invited_user_ids(room_id).await?.len(), 1); - assert_eq!(store.get_joined_user_ids(room_id).await?.len(), 1); + assert_eq!(store.get_invited_user_ids(room_id).await?.len(), 1, "Exepcted to find 1 invited user ids"); + assert_eq!(store.get_joined_user_ids(room_id).await?.len(), 1, "Exepcted to find 1 joined user ids"); assert_eq!(store.get_users_with_display_name(room_id, "example").await?.len(), 2, "expected to find 2 display names for room"); assert!(store .get_room_account_data_event(room_id, RoomAccountDataEventType::Tag) @@ -302,8 +302,7 @@ macro_rules! statestore_integration_tests { .get_event_room_receipt_events(room_id, ReceiptType::Read, first_receipt_event_id()) .await? .len(), - 1 - ); + 1, "Exepcted to find 1 read receipt"); Ok(()) } @@ -407,14 +406,15 @@ macro_rules! statestore_integration_tests { .get_user_room_receipt_event(room_id, ReceiptType::Read, user_id()) .await .expect("failed to read user room receipt after save") - .is_some(),); + .is_some()); assert_eq!( store .get_event_room_receipt_events(room_id, ReceiptType::Read, &first_event_id) .await .expect("failed to read user room receipt for 1 after save") .len(), - 1 + 1, + "Found a wrong number of receipts for 1 after save" ); assert!(store .get_event_room_receipt_events(room_id, ReceiptType::Read, &second_event_id) @@ -442,7 +442,8 @@ macro_rules! statestore_integration_tests { .await .expect("Getting event room receipt events for second event failed") .len(), - 1 + 1, + "Found a wrong number of receipts for second event after save" ); } diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 58a367d6c..30f2143e0 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -14,7 +14,6 @@ use std::{ collections::BTreeSet, - convert::TryInto, path::{Path, PathBuf}, sync::Arc, time::Instant, @@ -133,30 +132,14 @@ const TIMELINE: &str = "timeline"; const TIMELINE_METADATA: &str = "timeline-metadata"; const ROOM_EVENT_ID_POSITION: &str = "room-event-id-to-position"; - type Result = std::result::Result; -/// Get the value at `position` in encoded `key`. -/// -/// The key must have been encoded with the `EncodeKey` trait. `position` -/// corresponds to the position in the tuple before the key was encoded. If it -/// wasn't encoded in a tuple, use `0`. -/// -/// Returns `None` if there is no key at `position`. -fn decode_key_value(key: &[u8], position: usize) -> Option { - let values: Vec<&[u8]> = key.split(|v| *v == ENCODE_SEPARATOR).collect(); - - values.get(position).map(|s| String::from_utf8_lossy(s).to_string()) -} - - pub fn encode_key_with_usize>(s: A, i: usize) -> Vec { // FIXME: Not portable across architectures [s.as_ref().as_bytes(), &[ENCODE_SEPARATOR], i.to_be_bytes().as_ref(), &[ENCODE_SEPARATOR]] .concat() } - #[derive(Clone)] pub struct SledStore { path: Option, @@ -559,7 +542,7 @@ impl SledStore { // Add the receipt to the room event receipts room_event_receipts.insert( self.encode_key(ROOM_EVENT_RECEIPT, &(room, receipt_type, event_id, user_id)), - self.serialize_event(receipt) + self.serialize_event(&(user_id, receipt)) .map_err(ConflictableTransactionError::Abort)?, )?; } @@ -778,15 +761,10 @@ impl SledStore { spawn_blocking(move || { db.room_event_receipts .scan_prefix(key) + .values() .map(|u| { - u.map_err(|e| StoreError::Backend(anyhow!(e))).and_then(|(key, value)| { - db.deserialize_event(&value) - // TODO remove this unwrapping - .map(|receipt| { - (decode_key_value(&key, 3).unwrap().try_into().unwrap(), receipt) - }) - .map_err(|e| StoreError::Backend(anyhow!(e))) - }) + let v = u.map_err(|e| StoreError::Backend(anyhow!(e)))?; + db.deserialize_event(&v).map_err(|e| StoreError::Backend(anyhow!(e))) }) .collect() }) From cebe7bee92db389a20d5b37d19713122175caa56 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 13:11:59 +0200 Subject: [PATCH 08/32] adding encrypted tests for indexeddb store --- .../matrix-sdk-indexeddb/src/state_store.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index ad40aff8d..5fbaafbb7 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -198,6 +198,12 @@ impl IndexeddbStore { Ok(IndexeddbStore::open_helper("state".to_owned(), None).await?) } + #[allow(dead_code)] + pub(crate) async fn open_encrypted() -> StoreResult { + let key = StoreKey::new().map_err::(|e| e.into())?; + Ok(IndexeddbStore::open_helper("state_encrypted".to_owned(), Some(key)).await?) + } + pub async fn open_with_passphrase(name: String, passphrase: &str) -> StoreResult { Ok(Self::inner_open_with_passphrase(name, passphrase).await?) } @@ -1275,3 +1281,20 @@ mod tests { statestore_integration_tests! { integration } } + +#[cfg(test)] +mod encrypted_tests { + #[cfg(target_arch = "wasm32")] + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + + use matrix_sdk_base::statestore_integration_tests; + + use super::{IndexeddbStore, Result}; + + async fn get_store() -> Result { + Ok(IndexeddbStore::open_encrypted().await?) + } + + statestore_integration_tests! { integration } +} + From b3c8f80b6efc1822a789ec0ef18ab1f8539135ba Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 16:50:44 +0200 Subject: [PATCH 09/32] expose internal functionality for wasm-based storage --- crates/matrix-sdk-store-encryption/src/lib.rs | 155 ++++++++++++++++-- 1 file changed, 144 insertions(+), 11 deletions(-) diff --git a/crates/matrix-sdk-store-encryption/src/lib.rs b/crates/matrix-sdk-store-encryption/src/lib.rs index d3e54282a..1adeb33a3 100644 --- a/crates/matrix-sdk-store-encryption/src/lib.rs +++ b/crates/matrix-sdk-store-encryption/src/lib.rs @@ -284,16 +284,82 @@ impl StoreCipher { /// # Result::<_, anyhow::Error>::Ok(()) }; /// ``` pub fn encrypt_value(&self, value: &impl Serialize) -> Result, Error> { - let mut data = serde_json::to_vec(value)?; + Ok(serde_json::to_vec(&self.encrypt_value_typed(value)?)?) + } + /// Encrypt a value before it is inserted into the key/value store. + /// + /// A value can be decrypted using the [`StoreCipher::decrypt_value_typed()`] + /// method. This is the lower level function to `encrypt_value`, but returns the + /// full `EncryptdValue`-type + /// + /// # Arguments + /// + /// * `value` - A value that should be encrypted, any value that implements + /// `Serialize` can be given to this method. The value will be serialized as + /// json before it is encrypted. + /// + /// + /// # Examples + /// + /// ``` + /// # let example = || { + /// use matrix_sdk_store_encryption::StoreCipher; + /// use serde_json::{json, value::Value}; + /// + /// let store_cipher = StoreCipher::new()?; + /// + /// let value = json!({ + /// "some": "data", + /// }); + /// + /// let encrypted = store_cipher.encrypt_value_typed(&value)?; + /// let decrypted: Value = store_cipher.decrypt_value_typed(&encrypted)?; + /// + /// assert_eq!(value, decrypted); + /// # Result::<_, anyhow::Error>::Ok(()) }; + /// ``` + pub fn encrypt_value_typed(&self, value: &impl Serialize) -> Result { + let data = serde_json::to_vec(value)?; + Ok(self.encrypt_value_data(data)?) + } + + /// Encrypt some data before it is inserted into the key/value store. + /// + /// A value can be decrypted using the [`StoreCipher::decrypt_value_data()`] + /// method. This is the lower level function to `encrypt_value` + /// + /// # Arguments + /// + /// * `data` - A value that should be encrypted, encoded as a `Vec` + /// + /// # Examples + /// + /// ``` + /// # let example = || { + /// use matrix_sdk_store_encryption::StoreCipher; + /// use serde_json::{json, value::Value}; + /// + /// let store_cipher = StoreCipher::new()?; + /// + /// let value = serde_json::to_vec(json!({ + /// "some": "data", + /// })); + /// + /// let encrypted = store_cipher.encrypt_value_data(&value)?; + /// let decrypted: Value = store_cipher.decrypt_value_data(&encrypted)?; + /// + /// assert_eq!(value, decrypted); + /// # Result::<_, anyhow::Error>::Ok(()) }; + /// ``` + pub fn encrypt_value_data(&self, mut data: Vec) -> Result { let nonce = Keys::get_nonce()?; let cipher = XChaCha20Poly1305::new(self.inner.encryption_key()); let ciphertext = cipher.encrypt(XNonce::from_slice(&nonce), data.as_ref())?; data.zeroize(); - - Ok(serde_json::to_vec(&EncryptedValue { version: VERSION, ciphertext, nonce })?) + Ok(EncryptedValue { version: VERSION, ciphertext, nonce }) } /// Decrypt a value after it was fetchetd from the key/value store. @@ -325,22 +391,88 @@ impl StoreCipher { /// /// assert_eq!(value, decrypted); /// # Result::<_, anyhow::Error>::Ok(()) }; + /// ``` pub fn decrypt_value Deserialize<'b>>(&self, value: &[u8]) -> Result { let value: EncryptedValue = serde_json::from_slice(value)?; + self.decrypt_value_typed(value) + } + /// Decrypt a value after it was fetchetd from the key/value store. + /// + /// A value can be encrypted using the [`StoreCipher::encrypt_value_typed()`] + /// method. Lower level method to [`StoreCipher::decrypt_value_typed()`] + /// + /// # Arguments + /// + /// * `value` - The EncryptedValue of a value that should be decrypted. + /// + /// The method will deserialize the decrypted value into the expected type. + /// + /// # Examples + /// + /// ``` + /// # let example = || { + /// use matrix_sdk_store_encryption::StoreCipher; + /// use serde_json::{json, value::Value}; + /// + /// let store_cipher = StoreCipher::new()?; + /// + /// let value = json!({ + /// "some": "data", + /// }); + /// + /// let encrypted = store_cipher.encrypt_value_typed(&value)?; + /// let decrypted: Value = store_cipher.decrypt_value_typed(&encrypted)?; + /// + /// assert_eq!(value, decrypted); + /// # Result::<_, anyhow::Error>::Ok(()) }; + /// ``` + pub fn decrypt_value_typed Deserialize<'b>>(&self, value: EncryptedValue) -> Result { + let mut plaintext = self.decrypt_value_data(value)?; + let ret = serde_json::from_slice(&plaintext); + plaintext.zeroize(); + Ok(ret?) + } + + + /// Decrypt a value after it was fetchetd from the key/value store. + /// + /// A value can be encrypted using the [`StoreCipher::encrypt_value_data()`] + /// method. Lower level method to [`StoreCipher::decrypt_value()`]. + /// + /// # Arguments + /// + /// * `value` - The EncryptedValue of a value that should be decrypted. + /// + /// The method will return the raw decrypted value + /// + /// # Examples + /// + /// ``` + /// # let example = || { + /// use matrix_sdk_store_encryption::StoreCipher; + /// use serde_json::{json, value::Value}; + /// + /// let store_cipher = StoreCipher::new()?; + /// + /// let value = serde_json::to_vec(json!({ + /// "some": "data", + /// })); + /// + /// let encrypted = store_cipher.encrypt_value_data(&value)?; + /// let decrypted = store_cipher.decrypt_value_data(&encrypted)?; + /// + /// assert_eq!(value, decrypted); + /// # Result::<_, anyhow::Error>::Ok(()) }; + /// ``` + pub fn decrypt_value_data(&self, value: EncryptedValue) -> Result, Error> { if value.version != VERSION { return Err(Error::Version(VERSION, value.version)); } let cipher = XChaCha20Poly1305::new(self.inner.encryption_key()); let nonce = XNonce::from_slice(&value.nonce); - let mut plaintext = cipher.decrypt(nonce, value.ciphertext.as_ref())?; - - let ret = serde_json::from_slice(&plaintext); - - plaintext.zeroize(); - - Ok(ret?) + Ok(cipher.decrypt(nonce, value.ciphertext.as_ref())?) } /// Expand the given passphrase into a KEY_SIZE long key. @@ -362,8 +494,9 @@ impl MacKey { } } +/// Encrypted value, ready for storage, as created by the [`StoreCipher::encrypt_value_data()`] #[derive(Debug, Serialize, Deserialize, PartialEq)] -struct EncryptedValue { +pub struct EncryptedValue { version: u8, ciphertext: Vec, nonce: [u8; XNONCE_SIZE], From 1cdde23bc46e206aa5e7aeea4c782d69170ec3f1 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 16:50:58 +0200 Subject: [PATCH 10/32] replace storeky with storecipher in wasm --- .../matrix-sdk-indexeddb/src/state_store.rs | 65 ++++++++----------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 5fbaafbb7..d386ab3c5 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -21,11 +21,12 @@ use matrix_sdk_base::{ deserialized_responses::{MemberEvent, SyncRoomEvent}, media::{MediaRequest, UniqueKey}, store::{ - store_key::{self, EncryptedEvent, StoreKey}, BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError, }, RoomInfo, }; +use matrix_sdk_store_encryption::{StoreCipher, Error as EncryptionError}; + use matrix_sdk_common::{ async_trait, ruma::{ @@ -49,18 +50,15 @@ use wasm_bindgen::JsValue; use crate::safe_encode::SafeEncode; -#[derive(Debug, Serialize, Deserialize)] -pub enum DatabaseType { - Unencrypted, - Encrypted(store_key::EncryptedStoreKey), -} +#[derive(Clone, Serialize, Deserialize)] +struct StoreKeyWrapper(Vec); #[derive(Debug, thiserror::Error)] pub enum SerializationError { #[error(transparent)] Json(#[from] serde_json::Error), #[error(transparent)] - Encryption(#[from] store_key::Error), + Encryption(#[from] EncryptionError), #[error("DomException {name} ({code}): {message}")] DomException { name: String, message: String, code: u16 }, #[error(transparent)] @@ -83,9 +81,11 @@ impl From for StoreError { SerializationError::Json(e) => StoreError::Json(e), SerializationError::StoreError(e) => e, SerializationError::Encryption(e) => match e { - store_key::Error::Random(e) => StoreError::Encryption(e.to_string()), - store_key::Error::Serialization(e) => StoreError::Json(e), - store_key::Error::Encryption(e) => StoreError::Encryption(e), + EncryptionError::Random(e) => StoreError::Encryption(e.to_string()), + EncryptionError::Serialization(e) => StoreError::Json(e), + EncryptionError::Encryption(e) => StoreError::Encryption(e.to_string()), + EncryptionError::Version(found, expected) => StoreError::Encryption(format!("Bad Database Encryption Version: expected {} found {}", expected, found)), + EncryptionError::Length(found, expected) => StoreError::Encryption(format!("The database key an invalid length: expected {} found {}", expected, found)), }, _ => StoreError::Backend(anyhow!(e)), } @@ -135,7 +135,7 @@ mod KEYS { pub struct IndexeddbStore { name: String, pub(crate) inner: IdbDatabase, - store_key: Option, + store_cipher: Option, } impl std::fmt::Debug for IndexeddbStore { @@ -147,7 +147,7 @@ impl std::fmt::Debug for IndexeddbStore { type Result = std::result::Result; impl IndexeddbStore { - async fn open_helper(name: String, store_key: Option) -> Result { + async fn open_helper(name: String, store_cipher: Option) -> Result { // Open my_db v1 let mut db_req: OpenDbRequest = IdbDatabase::open_f64(&name, 1.0)?; db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> { @@ -190,7 +190,7 @@ impl IndexeddbStore { let db: IdbDatabase = db_req.into_future().await?; - Ok(Self { name, inner: db, store_key }) + Ok(Self { name, inner: db, store_cipher }) } #[allow(dead_code)] @@ -200,7 +200,7 @@ impl IndexeddbStore { #[allow(dead_code)] pub(crate) async fn open_encrypted() -> StoreResult { - let key = StoreKey::new().map_err::(|e| e.into())?; + let key = StoreCipher::new().map_err::(|e| e.into())?; Ok(IndexeddbStore::open_helper("state_encrypted".to_owned(), Some(key)).await?) } @@ -228,33 +228,25 @@ impl IndexeddbStore { db.transaction_on_one_with_mode("matrix-sdk-state", IdbTransactionMode::Readwrite)?; let ob = tx.object_store("matrix-sdk-state")?; - let store_key: Option = ob + let cipher = if let Some(StoreKeyWrapper(inner)) = ob .get(&JsValue::from_str(KEYS::STORE_KEY))? .await? - .map(|k| k.into_serde()) - .transpose()?; - - let store_key = if let Some(key) = store_key { - if let DatabaseType::Encrypted(k) = key { - StoreKey::import(passphrase, k).map_err(|_| StoreError::StoreLocked)? - } else { - return Err(StoreError::UnencryptedStore.into()); - } + .map(|v| v.into_serde()) + .transpose()? + { + StoreCipher::import(passphrase, &inner)? } else { - let key = StoreKey::new().map_err::(|e| e.into())?; - let encrypted_key = DatabaseType::Encrypted( - key.export(passphrase).map_err::(|e| e.into())?, - ); + let ciph = StoreCipher::new()?; ob.put_key_val( &JsValue::from_str(KEYS::STORE_KEY), - &JsValue::from_serde(&encrypted_key)?, + &JsValue::from_serde(&StoreKeyWrapper(ciph.export(passphrase)?))?, )?; - key + ciph }; tx.await.into_result()?; - IndexeddbStore::open_helper(name, Some(store_key)).await + IndexeddbStore::open_helper(name, Some(cipher)).await } pub async fn open_with_name(name: String) -> StoreResult { @@ -265,8 +257,8 @@ impl IndexeddbStore { &self, event: &impl Serialize, ) -> std::result::Result { - Ok(match self.store_key { - Some(ref key) => JsValue::from_serde(&key.encrypt(event)?)?, + Ok(match self.store_cipher { + Some(ref cipher) => JsValue::from_serde(&cipher.encrypt_value_typed(event)?)?, None => JsValue::from_serde(event)?, }) } @@ -275,11 +267,8 @@ impl IndexeddbStore { &self, event: JsValue, ) -> std::result::Result { - match self.store_key { - Some(ref key) => { - let encrypted: EncryptedEvent = event.into_serde()?; - Ok(key.decrypt(encrypted)?) - } + match self.store_cipher { + Some(ref cipher) => Ok(cipher.decrypt_value_typed(event.into_serde()?)?), None => Ok(event.into_serde()?), } } From b611aa250331696482d363c2746e64fa00d8f9e6 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 16:58:56 +0200 Subject: [PATCH 11/32] remove store key feature and references --- crates/matrix-sdk-base/Cargo.toml | 10 +- crates/matrix-sdk-base/README.md | 1 - crates/matrix-sdk-base/src/store/mod.rs | 3 - crates/matrix-sdk-base/src/store/store_key.rs | 299 ------------------ crates/matrix-sdk-indexeddb/Cargo.toml | 2 +- crates/matrix-sdk-sled/Cargo.toml | 2 +- 6 files changed, 3 insertions(+), 314 deletions(-) delete mode 100644 crates/matrix-sdk-base/src/store/store_key.rs diff --git a/crates/matrix-sdk-base/Cargo.toml b/crates/matrix-sdk-base/Cargo.toml index d786c5550..e58c73b3c 100644 --- a/crates/matrix-sdk-base/Cargo.toml +++ b/crates/matrix-sdk-base/Cargo.toml @@ -17,17 +17,9 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = [] -encryption = ["matrix-sdk-crypto", "store_key"] +encryption = ["matrix-sdk-crypto"] qrcode = ["matrix-sdk-crypto/qrcode"] -# Store Key is a helper feature for state store implementations -store_key = [ - "pbkdf2", - "hmac", - "sha2", - "rand", - "chacha20poly1305", -] # helpers for testing features build upon this testing = [ "http" ] diff --git a/crates/matrix-sdk-base/README.md b/crates/matrix-sdk-base/README.md index e5c1ecf4f..af30e9be3 100644 --- a/crates/matrix-sdk-base/README.md +++ b/crates/matrix-sdk-base/README.md @@ -7,5 +7,4 @@ The following crate feature flags are available: * `encryption`: Enables end-to-end encryption support in the library. * `qrcode`: Enbles QRcode generation and reading code -* `store_key`: Provides extra facilities for StateStores to manage store keys * `testing`: provides facilities and functions for tests, in particular for integration testing store implementations. ATTENTION: do not ever use outside of tests, we do not provide any stability warantees on these, these are merely helpers. If you find you _need_ any function provided here outside of tests, please open a Github Issue and inform us about your use case for us to consider. \ No newline at end of file diff --git a/crates/matrix-sdk-base/src/store/mod.rs b/crates/matrix-sdk-base/src/store/mod.rs index 50dd0e36f..8869fff83 100644 --- a/crates/matrix-sdk-base/src/store/mod.rs +++ b/crates/matrix-sdk-base/src/store/mod.rs @@ -50,9 +50,6 @@ use ruma::{ EventId, MxcUri, RoomId, UserId, }; -#[cfg(feature = "store_key")] -pub mod store_key; - /// BoxStream of owned Types pub type BoxStream = Pin + Send>>; diff --git a/crates/matrix-sdk-base/src/store/store_key.rs b/crates/matrix-sdk-base/src/store/store_key.rs deleted file mode 100644 index 49729881c..000000000 --- a/crates/matrix-sdk-base/src/store/store_key.rs +++ /dev/null @@ -1,299 +0,0 @@ -// 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. - -//! Facilities for StateStore implementations to reuse to manage encrypted -//! state keys - -use std::convert::TryFrom; - -use chacha20poly1305::{ - aead::{Aead, Error as EncryptionError, NewAead}, - ChaCha20Poly1305, Key, Nonce, XChaCha20Poly1305, XNonce, -}; -use hmac::Hmac; -use pbkdf2::pbkdf2; -use rand::{thread_rng, Error as RngError, Fill}; -use serde::{Deserialize, Serialize}; -use sha2::Sha256; -use zeroize::{Zeroize, Zeroizing}; - -use crate::StoreError; - -const VERSION: u8 = 1; -const KEY_SIZE: usize = 32; -const NONCE_SIZE: usize = 12; -const XNONCE_SIZE: usize = 24; -const KDF_SALT_SIZE: usize = 32; -#[cfg(not(test))] -const KDF_ROUNDS: u32 = 200_000; -#[cfg(test)] -const KDF_ROUNDS: u32 = 1000; - -/// Local State Error for Store Keys -/// -/// provides facilities to directly convert into a `StoreError` thus the most -/// common usage is to `.map_err(StoreError::into)` it. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// A problem de/serializing the JSON - #[error(transparent)] - Serialization(#[from] serde_json::Error), - /// A problem with en/decrypting - #[error("Error encrypting or decrypting an event {0}")] - Encryption(String), - /// Error generating enough random data for a cryptographic operation - #[error("Error generating enough random data for a cryptographic operation")] - Random(#[from] RngError), -} - -#[allow(clippy::from_over_into)] -impl Into for Error { - fn into(self) -> StoreError { - match self { - Error::Serialization(e) => StoreError::Json(e), - Error::Encryption(e) => StoreError::Encryption(e), - Error::Random(_) => StoreError::Encryption(self.to_string()), - } - } -} - -impl From for Error { - fn from(e: EncryptionError) -> Self { - Error::Encryption(e.to_string()) - } -} - -/// Holding the data of the encrypted event -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub struct EncryptedEvent { - version: u8, - ciphertext: Vec, - nonce: Vec, -} - -/// Version specific info for the key derivation method that is used. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -enum KdfInfo { - Pbkdf2ToChaCha20Poly1305 { - /// The number of PBKDF rounds that were used when deriving the store - /// key. - rounds: u32, - /// The salt that was used when the passphrase was expanded into a store - /// key. - kdf_salt: Vec, - }, -} - -/// Version specific info for encryption method that is used to encrypt our -/// store key. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -enum CipherTextInfo { - ChaCha20Poly1305 { - /// The nonce that was used to encrypt the ciphertext. - nonce: Vec, - /// The encrypted store key. - ciphertext: Vec, - }, -} - -/// An encrypted version of our store key, this can be safely stored in a -/// database. -#[derive(Debug, Serialize, Deserialize, PartialEq)] -pub struct EncryptedStoreKey { - /// Info about the key derivation method that was used to expand the - /// passphrase into an encryption key. - kdf_info: KdfInfo, - /// The ciphertext with it's accompanying additional data that is needed to - /// decrypt the store key. - ciphertext_info: CipherTextInfo, -} - -/// A store key that can be used to encrypt entries in the store. -#[derive(Debug, Zeroize, PartialEq)] -pub struct StoreKey { - inner: Vec, -} - -impl TryFrom> for StoreKey { - type Error = (); - - fn try_from(value: Vec) -> Result { - if value.len() != KEY_SIZE { - Err(()) - } else { - Ok(Self { inner: value }) - } - } -} - -impl StoreKey { - /// Generate a new random store key. - pub fn new() -> Result { - let mut key = vec![0u8; KEY_SIZE]; - let mut rng = thread_rng(); - key.try_fill(&mut rng)?; - - Ok(Self { inner: key }) - } - - /// Expand the given passphrase into a KEY_SIZE long key. - fn expand_key(passphrase: &str, salt: &[u8], rounds: u32) -> Zeroizing> { - let mut key = Zeroizing::from(vec![0u8; KEY_SIZE]); - pbkdf2::>(passphrase.as_bytes(), salt, rounds, &mut *key); - key - } - - /// Get the store key. - fn key(&self) -> &Key { - Key::from_slice(&self.inner) - } - - /// Encrypt and export our store key using the given passphrase. - /// - /// # Arguments - /// - /// * `passphrase` - The passphrase that should be used to encrypt the - /// store key. - pub fn export(&self, passphrase: &str) -> Result { - let mut rng = thread_rng(); - - let mut salt = vec![0u8; KDF_SALT_SIZE]; - salt.try_fill(&mut rng)?; - - let key = StoreKey::expand_key(passphrase, &salt, KDF_ROUNDS); - let key = Key::from_slice(key.as_ref()); - let cipher = ChaCha20Poly1305::new(key); - - let mut nonce = vec![0u8; NONCE_SIZE]; - nonce.try_fill(&mut rng)?; - - let ciphertext = - cipher.encrypt(Nonce::from_slice(nonce.as_ref()), self.inner.as_slice())?; - - Ok(EncryptedStoreKey { - kdf_info: KdfInfo::Pbkdf2ToChaCha20Poly1305 { rounds: KDF_ROUNDS, kdf_salt: salt }, - ciphertext_info: CipherTextInfo::ChaCha20Poly1305 { nonce, ciphertext }, - }) - } - - fn get_nonce() -> Result, RngError> { - let mut nonce = vec![0u8; XNONCE_SIZE]; - let mut rng = thread_rng(); - - nonce.try_fill(&mut rng)?; - - Ok(nonce) - } - - /// Encrypt the given Event after serializing it with serde_json - pub fn encrypt(&self, event: &impl Serialize) -> Result { - let event = serde_json::to_vec(event)?; - - let nonce = StoreKey::get_nonce()?; - let cipher = XChaCha20Poly1305::new(self.key()); - let xnonce = XNonce::from_slice(&nonce); - - let ciphertext = cipher.encrypt(xnonce, event.as_ref())?; - - Ok(EncryptedEvent { version: VERSION, ciphertext, nonce }) - } - - /// Decrypt the given encrypted event back into the inner - pub fn decrypt Deserialize<'b>>(&self, event: EncryptedEvent) -> Result { - if event.version != VERSION { - return Err(Error::Encryption( - "Error decrypting: Unknown ciphertext version".to_owned(), - )); - } - - let cipher = XChaCha20Poly1305::new(self.key()); - let nonce = XNonce::from_slice(&event.nonce); - let plaintext = cipher.decrypt(nonce, event.ciphertext.as_ref())?; - - Ok(serde_json::from_slice(&plaintext)?) - } - - /// Restore a store key from an encrypted export. - /// - /// # Arguments - /// - /// * `passphrase` - The passphrase that should be used to encrypt the - /// store key. - /// - /// * `encrypted` - The exported and encrypted version of the store key. - pub fn import(passphrase: &str, encrypted: EncryptedStoreKey) -> Result { - let key = match encrypted.kdf_info { - KdfInfo::Pbkdf2ToChaCha20Poly1305 { rounds, kdf_salt } => { - Self::expand_key(passphrase, &kdf_salt, rounds) - } - }; - - let key = Key::from_slice(key.as_ref()); - - let decrypted = match encrypted.ciphertext_info { - CipherTextInfo::ChaCha20Poly1305 { nonce, ciphertext } => { - let cipher = ChaCha20Poly1305::new(key); - let nonce = Nonce::from_slice(&nonce); - cipher.decrypt(nonce, ciphertext.as_ref())? - } - }; - - Ok(Self { inner: decrypted }) - } -} - -#[cfg(test)] -mod tests { - use serde_json::{json, Value}; - - use super::StoreKey; - - #[test] - fn generating() { - StoreKey::new().unwrap(); - } - - #[test] - fn encrypting() { - let passphrase = "it's a secret to everybody"; - let store_key = StoreKey::new().unwrap(); - - let encrypted = store_key.export(passphrase).unwrap(); - let decrypted = StoreKey::import(passphrase, encrypted).unwrap(); - - assert_eq!(store_key, decrypted); - } - - #[test] - fn encrypting_events() { - let event = json!({ - "content": { - "body": "Bee Gees - Stayin' Alive", - "info": { - "duration": 2140786, - "mimetype": "audio/mpeg", - "size": 1563685 - }, - "msgtype": "m.audio", - "url": "mxc://example.org/ffed755USFFxlgbQYZGtryd" - }, - }); - - let store_key = StoreKey::new().unwrap(); - - let encrypted = store_key.encrypt(&event).unwrap(); - let decrypted: Value = store_key.decrypt(encrypted).unwrap(); - assert_eq!(event, decrypted); - } -} diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml index 91d54e51c..6e177e9a3 100644 --- a/crates/matrix-sdk-indexeddb/Cargo.toml +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -11,7 +11,7 @@ encryption = ["matrix-sdk-base/encryption", "matrix-sdk-crypto"] default-target = "wasm32-unknown-unknown" [dependencies] -matrix-sdk-base = { path = "../matrix-sdk-base", features = ["store_key"] } +matrix-sdk-base = { path = "../matrix-sdk-base" } matrix-sdk-crypto = { path = "../matrix-sdk-crypto", optional = true } matrix-sdk-common = { path = "../matrix-sdk-common" } matrix-sdk-store-encryption = { path = "../matrix-sdk-store-encryption" } diff --git a/crates/matrix-sdk-sled/Cargo.toml b/crates/matrix-sdk-sled/Cargo.toml index f1c685e84..19582f757 100644 --- a/crates/matrix-sdk-sled/Cargo.toml +++ b/crates/matrix-sdk-sled/Cargo.toml @@ -12,7 +12,7 @@ crypto-store = ["matrix-sdk-crypto"] [dependencies] futures-core = "0.3.15" futures-util = { version = "0.3.15", default-features = false } -matrix-sdk-base = { path = "../matrix-sdk-base", features = ["store_key"], optional = true } +matrix-sdk-base = { path = "../matrix-sdk-base", optional = true } matrix-sdk-common = { path = "../matrix-sdk-common" } matrix-sdk-crypto = { path = "../matrix-sdk-crypto", optional = true } matrix-sdk-store-encryption = { path = "../matrix-sdk-store-encryption" } From 64e03567562a6662b5c15c6251d08f066d95a28c Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 19:17:49 +0200 Subject: [PATCH 12/32] implementing encrypted keys --- crates/matrix-sdk-indexeddb/Cargo.toml | 1 + .../matrix-sdk-indexeddb/src/safe_encode.rs | 81 +++++++ .../matrix-sdk-indexeddb/src/state_store.rs | 215 +++++++++--------- 3 files changed, 195 insertions(+), 102 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml index 6e177e9a3..0e5fb1921 100644 --- a/crates/matrix-sdk-indexeddb/Cargo.toml +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -22,6 +22,7 @@ futures-util = { version = "0.3.15", default-features = false } indexed_db_futures = { version = "0.2.0" } wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } web-sys = { version = "0.3.35", features = ["IdbKeyRange"] } +base64 = { version = "0.13.0" } serde = { version = "1.0.126" } serde_json = "1.0.64" dashmap = "5.1.0" diff --git a/crates/matrix-sdk-indexeddb/src/safe_encode.rs b/crates/matrix-sdk-indexeddb/src/safe_encode.rs index 1a6999a37..18f1bbe04 100644 --- a/crates/matrix-sdk-indexeddb/src/safe_encode.rs +++ b/crates/matrix-sdk-indexeddb/src/safe_encode.rs @@ -5,8 +5,10 @@ use matrix_sdk_base::ruma::events::{ use matrix_sdk_common::ruma::{ receipt::ReceiptType, DeviceId, EventId, MxcUri, RoomId, TransactionId, UserId, }; +use matrix_sdk_store_encryption::StoreCipher; use wasm_bindgen::JsValue; use web_sys::IdbKeyRange; +use base64::{STANDARD_NO_PAD, encode_config as base64_encode}; /// Helpers for wasm32/browser environments @@ -39,6 +41,18 @@ pub trait SafeEncode { JsValue::from(self.as_encoded_string()) } + /// Encode self into a Uint8Array for usage as a key + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> JsValue { + JsValue::from(self.as_secure_string(table_name, store_cipher)) + } + + fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String { + base64_encode( + store_cipher.hash_key(table_name, self.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ) + } + /// Encode self into a IdbKeyRange for searching all keys that are /// prefixed with this key, followed by `KEY_SEPARATOR`. Internally /// uses `as_encoded_string` to ensure the given key is escaped properly. @@ -50,6 +64,15 @@ pub trait SafeEncode { ) .map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned())) } + + fn encode_to_range_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Result { + let key = self.as_secure_string(table_name, store_cipher); + IdbKeyRange::bound( + &JsValue::from([&key, KEY_SEPARATOR].concat()), + &JsValue::from([&key, RANGE_END].concat()), + ) + .map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned())) + } } /// Implement SafeEncode for tuple of two elements, separating the escaped @@ -62,6 +85,21 @@ where fn as_encoded_string(&self) -> String { [&self.0.as_encoded_string(), KEY_SEPARATOR, &self.1.as_encoded_string()].concat() } + + fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String { + [ + &base64_encode( + store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR, + &base64_encode( + store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR + ].concat() + } } /// Implement SafeEncode for tuple of three elements, separating the escaped @@ -82,6 +120,25 @@ where ] .concat() } + + fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String { + [ + &base64_encode( + store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR, + &base64_encode( + store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR, + &base64_encode( + store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + ].concat() + } } /// Implement SafeEncode for tuple of four elements, separating the escaped @@ -105,6 +162,30 @@ where ] .concat() } + + fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String { + [ + &base64_encode( + store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR, + &base64_encode( + store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR, + &base64_encode( + store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + KEY_SEPARATOR, + &base64_encode( + store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes()), + STANDARD_NO_PAD + ), + ].concat() + } } impl SafeEncode for String { diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index d386ab3c5..09cd6980e 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -47,6 +47,7 @@ use matrix_sdk_common::{ use serde::{Deserialize, Serialize}; use tracing::{info, warn}; use wasm_bindgen::JsValue; +use web_sys::IdbKeyRange; use crate::safe_encode::SafeEncode; @@ -273,6 +274,25 @@ impl IndexeddbStore { } } + fn encode_key(&self, table_name: &str, key: T) -> JsValue + where T: SafeEncode + { + match self.store_cipher { + Some(ref cipher) => key.encode_secure(table_name, cipher), + None => key.encode(), + } + } + + fn encode_to_range(&self, table_name: &str, key: T) -> Result + where T: SafeEncode + { + match self.store_cipher { + Some(ref cipher) => key.encode_to_range_secure(table_name, cipher), + None => key.encode_to_range() + }.map_err(|e| SerializationError::StoreError(StoreError::Backend(anyhow!(e)))) + } + + pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { let tx = self .inner @@ -280,7 +300,7 @@ impl IndexeddbStore { let obj = tx.object_store(KEYS::SESSION)?; - obj.put_key_val(&(KEYS::FILTER, filter_name).encode(), &JsValue::from_str(filter_id))?; + obj.put_key_val(&self.encode_key(KEYS::FILTER, (KEYS::FILTER, filter_name)), &JsValue::from_str(filter_id))?; tx.await.into_result()?; @@ -292,7 +312,7 @@ impl IndexeddbStore { .inner .transaction_on_one_with_mode(KEYS::SESSION, IdbTransactionMode::Readonly)? .object_store(KEYS::SESSION)? - .get(&(KEYS::FILTER, filter_name).encode())? + .get(&self.encode_key(KEYS::FILTER, (KEYS::FILTER, filter_name)))? .await? .and_then(|f| f.as_string())) } @@ -365,7 +385,7 @@ impl IndexeddbStore { let store = tx.object_store(KEYS::DISPLAY_NAMES)?; for (room_id, ambiguity_maps) in &changes.ambiguity_maps { for (display_name, map) in ambiguity_maps { - let key = (room_id, display_name).encode(); + let key = self.encode_key(KEYS::DISPLAY_NAMES, (room_id, display_name)); store.put_key_val(&key, &self.serialize_event(&map)?)?; } @@ -375,7 +395,9 @@ impl IndexeddbStore { if !changes.account_data.is_empty() { let store = tx.object_store(KEYS::ACCOUNT_DATA)?; for (event_type, event) in &changes.account_data { - store.put_key_val(&event_type.encode(), &self.serialize_event(&event)?)?; + store.put_key_val( + &self.encode_key(KEYS::ACCOUNT_DATA, event_type), + &self.serialize_event(&event)?)?; } } @@ -383,7 +405,7 @@ impl IndexeddbStore { let store = tx.object_store(KEYS::ROOM_ACCOUNT_DATA)?; for (room, events) in &changes.room_account_data { for (event_type, event) in events { - let key = (room, event_type).encode(); + let key = self.encode_key(KEYS::ROOM_ACCOUNT_DATA, (room, event_type)); store.put_key_val(&key, &self.serialize_event(&event)?)?; } } @@ -394,7 +416,7 @@ impl IndexeddbStore { for (room, event_types) in &changes.state { for (event_type, events) in event_types { for (state_key, event) in events { - let key = (room, event_type, state_key).encode(); + let key = self.encode_key(KEYS::ROOM_STATE, (room, event_type, state_key)); store.put_key_val(&key, &self.serialize_event(&event)?)?; } } @@ -404,21 +426,29 @@ impl IndexeddbStore { if !changes.room_infos.is_empty() { let store = tx.object_store(KEYS::ROOM_INFOS)?; for (room_id, room_info) in &changes.room_infos { - store.put_key_val(&room_id.encode(), &self.serialize_event(&room_info)?)?; + store.put_key_val( + &self.encode_key(KEYS::ROOM_INFOS, room_id), + &self.serialize_event(&room_info)?)?; } } if !changes.presence.is_empty() { let store = tx.object_store(KEYS::PRESENCE)?; for (sender, event) in &changes.presence { - store.put_key_val(&sender.encode(), &self.serialize_event(&event)?)?; + store.put_key_val( + &self.encode_key(KEYS::PRESENCE, sender), + &self.serialize_event(&event)? + )?; } } if !changes.stripped_room_infos.is_empty() { let store = tx.object_store(KEYS::STRIPPED_ROOM_INFOS)?; for (room_id, info) in &changes.stripped_room_infos { - store.put_key_val(&room_id.encode(), &self.serialize_event(&info)?)?; + store.put_key_val( + &self.encode_key(KEYS::STRIPPED_ROOM_INFOS, room_id), + &self.serialize_event(&info)? + )?; } } @@ -426,7 +456,7 @@ impl IndexeddbStore { let store = tx.object_store(KEYS::STRIPPED_MEMBERS)?; for (room, events) in &changes.stripped_members { for event in events.values() { - let key = (room, &event.state_key).encode(); + let key = self.encode_key(KEYS::STRIPPED_MEMBERS, (room, &event.state_key)); store.put_key_val(&key, &self.serialize_event(&event)?)?; } } @@ -437,7 +467,7 @@ impl IndexeddbStore { for (room, event_types) in &changes.stripped_state { for (event_type, events) in event_types { for (state_key, event) in events { - let key = (room, event_type, state_key).encode(); + let key = self.encode_key(KEYS::STRIPPED_ROOM_STATE, (room, event_type, state_key)); store.put_key_val(&key, &self.serialize_event(&event)?)?; } } @@ -454,27 +484,33 @@ impl IndexeddbStore { let members = tx.object_store(KEYS::MEMBERS)?; for event in events.values() { - let key = (room, &event.state_key).encode(); + let key = (room, &event.state_key); match event.content.membership { MembershipState::Join => { - joined.put_key_val_owned(&key, &event.state_key.encode())?; - invited.delete(&key)?; + joined.put_key_val_owned( + &self.encode_key(KEYS::JOINED_USER_IDS, key), + &self.serialize_event(&event.state_key)? + )?; + invited.delete(&self.encode_key(KEYS::INVITED_USER_IDS, key))?; } MembershipState::Invite => { - invited.put_key_val_owned(&key, &event.state_key.encode())?; - joined.delete(&key)?; + invited.put_key_val_owned( + &self.encode_key(KEYS::INVITED_USER_IDS, key), + &self.serialize_event(&event.state_key)? + )?; + joined.delete(&self.encode_key(KEYS::JOINED_USER_IDS, key))?; } _ => { - joined.delete(&key)?; - invited.delete(&key)?; + joined.delete(&self.encode_key(KEYS::JOINED_USER_IDS, key))?; + invited.delete(&self.encode_key(KEYS::INVITED_USER_IDS, key))?; } } - members.put_key_val_owned(&key, &self.serialize_event(&event)?)?; + members.put_key_val_owned(&self.encode_key(KEYS::MEMBERS, key), &self.serialize_event(&event)?)?; if let Some(profile) = profile_changes.and_then(|p| p.get(&event.state_key)) { - profiles_store.put_key_val_owned(&key, &self.serialize_event(&profile)?)?; + profiles_store.put_key_val_owned(&self.encode_key(KEYS::PROFILES, key), &self.serialize_event(&profile)?)?; } } } @@ -488,7 +524,7 @@ impl IndexeddbStore { for (event_id, receipts) in &content.0 { for (receipt_type, receipts) in receipts { for (user_id, receipt) in receipts { - let key = (room, receipt_type, user_id).encode(); + let key = self.encode_key(KEYS::ROOM_USER_RECEIPTS, (room, receipt_type, user_id)); if let Some((old_event, _)) = room_user_receipts.get(&key)?.await?.and_then(|f| { @@ -496,7 +532,7 @@ impl IndexeddbStore { }) { room_event_receipts - .delete(&(room, receipt_type, &old_event, user_id).encode())?; + .delete(&self.encode_key(KEYS::ROOM_EVENT_RECEIPTS, (room, receipt_type, &old_event, user_id)))?; } room_user_receipts @@ -504,8 +540,8 @@ impl IndexeddbStore { // Add the receipt to the room event receipts room_event_receipts.put_key_val( - &(room, receipt_type, event_id, user_id).encode(), - &self.serialize_event(&receipt)?, + &self.encode_key(KEYS::ROOM_EVENT_RECEIPTS, (room, receipt_type, event_id, user_id)), + &self.serialize_event(&(user_id, receipt))?, )?; } } @@ -525,18 +561,19 @@ impl IndexeddbStore { } else { info!("Save new timeline batch from messages response for {}", room_id); } - let room_key = room_id.encode(); - let metadata: Option = if timeline.limited { info!( "Delete stored timeline for {} because the sync response was limited", room_id ); - let range = room_id.encode_to_range().map_err(StoreError::Codec)?; - let stores = - &[&timeline_store, &timeline_metadata_store, &event_id_to_position_store]; - for store in stores { + let stores = &[ + (KEYS::ROOM_TIMELINE, &timeline_store), + (KEYS::ROOM_TIMELINE_METADATA, &timeline_metadata_store), + (KEYS::ROOM_EVENT_ID_TO_POSITION, &event_id_to_position_store) + ]; + for (table_name, store) in stores { + let range = self.encode_to_range(table_name, room_id)?; for key in store.get_all_keys_with_key(&range)?.await?.iter() { store.delete(&key)?; } @@ -545,7 +582,7 @@ impl IndexeddbStore { None } else { let metadata: Option = timeline_metadata_store - .get(&room_key)? + .get(&self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id))? .await? .map(|v| v.into_serde()) .transpose()?; @@ -562,7 +599,7 @@ impl IndexeddbStore { let mut delete_timeline = false; for event in &timeline.events { if let Some(event_id) = event.event_id() { - let event_key = (room_id, &event_id).encode(); + let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &event_id)); if event_id_to_position_store .count_with_key_owned(event_key)? .await? @@ -580,13 +617,14 @@ impl IndexeddbStore { room_id ); - let range = room_id.encode_to_range().map_err(StoreError::Codec)?; + let stores = &[ - &timeline_store, - &timeline_metadata_store, - &event_id_to_position_store, + (KEYS::ROOM_TIMELINE, &timeline_store), + (KEYS::ROOM_TIMELINE_METADATA, &timeline_metadata_store), + (KEYS::ROOM_EVENT_ID_TO_POSITION, &event_id_to_position_store) ]; - for store in stores { + for (table_name, store) in stores { + let range = self.encode_to_range(table_name, room_id)?; for key in store.get_all_keys_with_key(&range)?.await?.iter() { store.delete(&key)?; } @@ -618,7 +656,7 @@ impl IndexeddbStore { if timeline.sync { let room_version = room_infos - .get(&room_key)? + .get(&self.encode_key(KEYS::ROOM_INFOS, room_id))? .await? .map(|r| self.deserialize_event::(r)) .transpose()? @@ -636,7 +674,7 @@ impl IndexeddbStore { AnySyncMessageLikeEvent::RoomRedaction(redaction), )) = event.event.deserialize() { - let redacts_key = (room_id, &redaction.redacts).encode(); + let redacts_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &redaction.redacts)); if let Some(position_key) = event_id_to_position_store.get_owned(redacts_key)?.await? { @@ -663,22 +701,22 @@ impl IndexeddbStore { } metadata.start_position -= 1; - let key = (room_id, &metadata.start_position).encode(); + let key = self.encode_key(KEYS::ROOM_TIMELINE, (room_id, &metadata.start_position)); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = (room_id, &event_id).encode(); + let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &event_id)); event_id_to_position_store.put_key_val(&event_key, &key)?; } - timeline_store.put_key_val_owned(key, &self.serialize_event(&event)?)?; + timeline_store.put_key_val_owned(&key, &self.serialize_event(&event)?)?; } } else { for event in &timeline.events { metadata.end_position += 1; - let key = (room_id, &metadata.end_position).encode(); + let key = self.encode_key(KEYS::ROOM_TIMELINE, (room_id, &metadata.end_position)); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = (room_id, &event_id).encode(); + let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &event_id)); event_id_to_position_store.put_key_val(&event_key, &key)?; } @@ -687,7 +725,7 @@ impl IndexeddbStore { } timeline_metadata_store - .put_key_val_owned(room_key, &JsValue::from_serde(&metadata)?)?; + .put_key_val_owned(&self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id), &JsValue::from_serde(&metadata)?)?; } } @@ -698,7 +736,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::PRESENCE, IdbTransactionMode::Readonly)? .object_store(KEYS::PRESENCE)? - .get(&user_id.encode())? + .get(&self.encode_key(KEYS::PRESENCE, user_id))? .await? .map(|f| self.deserialize_event(f)) .transpose() @@ -713,7 +751,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::ROOM_STATE, IdbTransactionMode::Readonly)? .object_store(KEYS::ROOM_STATE)? - .get(&(room_id, &event_type, state_key).encode())? + .get(&self.encode_key(KEYS::ROOM_STATE, (room_id, &event_type, state_key)))? .await? .map(|f| self.deserialize_event(f)) .transpose() @@ -724,7 +762,7 @@ impl IndexeddbStore { room_id: &RoomId, event_type: StateEventType, ) -> Result>> { - let range = (room_id, &event_type).encode_to_range().map_err(StoreError::Codec)?; + let range = self.encode_to_range(KEYS::ROOM_STATE, (room_id, &event_type))?; Ok(self .inner .transaction_on_one_with_mode(KEYS::ROOM_STATE, IdbTransactionMode::Readonly)? @@ -744,7 +782,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::PROFILES, IdbTransactionMode::Readonly)? .object_store(KEYS::PROFILES)? - .get(&(room_id, user_id).encode())? + .get(&self.encode_key(KEYS::PROFILES, (room_id, user_id)))? .await? .map(|f| self.deserialize_event(f)) .transpose() @@ -758,31 +796,21 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::MEMBERS, IdbTransactionMode::Readonly)? .object_store(KEYS::MEMBERS)? - .get(&(room_id, state_key).encode())? + .get(&self.encode_key(KEYS::MEMBERS, (room_id, state_key)))? .await? .map(|f| self.deserialize_event(f)) .transpose() } pub async fn get_user_ids_stream(&self, room_id: &RoomId) -> Result>> { - let range = room_id.encode_to_range().map_err(StoreError::Codec)?; - let skip = room_id.as_encoded_string().len() + 1; - Ok(self - .inner - .transaction_on_one_with_mode(KEYS::MEMBERS, IdbTransactionMode::Readonly)? - .object_store(KEYS::MEMBERS)? - .get_all_keys_with_key(&range)? - .await? - .iter() - .filter_map(|key| match key.as_string() { - Some(k) => UserId::parse(&k[skip..]).ok(), - _ => None, - }) - .collect::>()) + Ok([ + self.get_invited_user_ids(room_id).await?, + self.get_joined_user_ids(room_id).await? + ].concat()) } pub async fn get_invited_user_ids(&self, room_id: &RoomId) -> Result>> { - let range = room_id.encode_to_range().map_err(StoreError::Codec)?; + let range = self.encode_to_range(KEYS::INVITED_USER_IDS, room_id)?; let entries = self .inner .transaction_on_one_with_mode(KEYS::INVITED_USER_IDS, IdbTransactionMode::Readonly)? @@ -797,7 +825,7 @@ impl IndexeddbStore { } pub async fn get_joined_user_ids(&self, room_id: &RoomId) -> Result>> { - let range = room_id.encode_to_range().map_err(StoreError::Codec)?; + let range = self.encode_to_range(KEYS::JOINED_USER_IDS, room_id)?; Ok(self .inner .transaction_on_one_with_mode(KEYS::JOINED_USER_IDS, IdbTransactionMode::Readonly)? @@ -845,7 +873,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::DISPLAY_NAMES, IdbTransactionMode::Readonly)? .object_store(KEYS::DISPLAY_NAMES)? - .get(&(room_id, display_name).encode())? + .get(&self.encode_key(KEYS::DISPLAY_NAMES, (room_id, display_name)))? .await? .map(|f| { self.deserialize_event::>>(f) @@ -861,7 +889,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::ACCOUNT_DATA, IdbTransactionMode::Readonly)? .object_store(KEYS::ACCOUNT_DATA)? - .get(&JsValue::from_str(&event_type.to_string()))? + .get(&self.encode_key(KEYS::ACCOUNT_DATA, event_type))? .await? .map(|f| self.deserialize_event(f).map_err::(|e| e)) .transpose() @@ -875,7 +903,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::ROOM_ACCOUNT_DATA, IdbTransactionMode::Readonly)? .object_store(KEYS::ROOM_ACCOUNT_DATA)? - .get(&(room_id.as_str(), &event_type.to_string()).encode())? + .get(&self.encode_key(KEYS::ROOM_ACCOUNT_DATA, (room_id, event_type)))? .await? .map(|f| self.deserialize_event(f).map_err::(|e| e)) .transpose() @@ -890,7 +918,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::ROOM_USER_RECEIPTS, IdbTransactionMode::Readonly)? .object_store(KEYS::ROOM_USER_RECEIPTS)? - .get(&(room_id.as_str(), receipt_type.as_ref(), user_id.as_str()).encode())? + .get(&self.encode_key(KEYS::ROOM_USER_RECEIPTS, (room_id, receipt_type, user_id)))? .await? .map(|f| self.deserialize_event(f)) .transpose() @@ -902,38 +930,23 @@ impl IndexeddbStore { receipt_type: ReceiptType, event_id: &EventId, ) -> Result, Receipt)>> { - let key = (room_id, &receipt_type, event_id); - let prefix_len = key.as_encoded_string().len() + 1; - let range = key.encode_to_range().map_err(StoreError::Codec)?; + let range = self.encode_to_range(KEYS::ROOM_EVENT_RECEIPTS, (room_id, &receipt_type, event_id))?; let tx = self.inner.transaction_on_one_with_mode( KEYS::ROOM_EVENT_RECEIPTS, IdbTransactionMode::Readonly, )?; let store = tx.object_store(KEYS::ROOM_EVENT_RECEIPTS)?; - let mut all = Vec::new(); - for k in store.get_all_keys_with_key(&range)?.await?.iter() { - // FIXME: we should probably parallelize this... - let res = store - .get(&k)? - .await? - .ok_or_else(|| StoreError::Codec(format!("no data at {:?}", k)))?; - let u = if let Some(k_str) = k.as_string() { - UserId::parse(&k_str[prefix_len..]) - .map_err(|e| StoreError::Codec(format!("{:?}", e)))? - } else { - return Err(StoreError::Codec(format!("{:?}", k)).into()); - }; - let r = self - .deserialize_event::(res) - .map_err(|e| StoreError::Codec(e.to_string()))?; - all.push((u, r)); - } - Ok(all) + Ok(store + .get_all_with_key(&range)? + .await? + .iter() + .filter_map(|f| self.deserialize_event(f).ok()) + .collect::>()) } async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { - let key = (&request.source.unique_key(), &request.format.unique_key()).encode(); + let key = self.encode_key(KEYS::MEDIA, request.format.unique_key()); let tx = self.inner.transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readwrite)?; @@ -943,7 +956,7 @@ impl IndexeddbStore { } async fn get_media_content(&self, request: &MediaRequest) -> Result>> { - let key = (&request.source.unique_key(), &request.format.unique_key()).encode(); + let key = self.encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); self.inner .transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readonly)? .object_store(KEYS::MEDIA)? @@ -987,7 +1000,7 @@ impl IndexeddbStore { } async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> { - let key = (&request.source.unique_key(), &request.format.unique_key()).encode(); + let key = self.encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); let tx = self.inner.transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readwrite)?; @@ -997,7 +1010,7 @@ impl IndexeddbStore { } async fn remove_media_content_for_uri(&self, uri: &MxcUri) -> Result<()> { - let range = uri.encode_to_range().map_err(StoreError::Codec)?; + let range = self.encode_to_range(KEYS::MEDIA, uri)?; let tx = self.inner.transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readwrite)?; let store = tx.object_store(KEYS::MEDIA)?; @@ -1040,14 +1053,13 @@ impl IndexeddbStore { .inner .transaction_on_multi_with_mode(&all_stores, IdbTransactionMode::Readwrite)?; - let room_key = room_id.encode(); for store_name in direct_stores { - tx.object_store(store_name)?.delete(&room_key)?; + tx.object_store(store_name)?.delete(&self.encode_key(store_name, room_id))?; } - let range = room_id.encode_to_range().map_err(StoreError::Codec)?; for store_name in prefixed_stores { let store = tx.object_store(store_name)?; + let range = self.encode_to_range(store_name, room_id)?; for key in store.get_all_keys_with_key(&range)?.await?.iter() { store.delete(&key)?; } @@ -1059,7 +1071,6 @@ impl IndexeddbStore { &self, room_id: &RoomId, ) -> Result>, Option)>> { - let key = room_id.encode(); let tx = self.inner.transaction_on_multi_with_mode( &[KEYS::ROOM_TIMELINE, KEYS::ROOM_TIMELINE_METADATA], IdbTransactionMode::Readonly, @@ -1068,7 +1079,7 @@ impl IndexeddbStore { let metadata = tx.object_store(KEYS::ROOM_TIMELINE_METADATA)?; let metadata: Option = - metadata.get(&key)?.await?.map(|v| v.into_serde()).transpose()?; + metadata.get(&self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id))?.await?.map(|v| v.into_serde()).transpose()?; if metadata.is_none() { info!("No timeline for {} was previously stored", room_id); return Ok(None); @@ -1076,7 +1087,7 @@ impl IndexeddbStore { let end_token = metadata.and_then(|m| m.end); #[allow(clippy::needless_collect)] let timeline: Vec> = timeline - .get_all_with_key(&key)? + .get_all_with_key(&self.encode_to_range(KEYS::ROOM_TIMELINE, room_id)?)? .await? .iter() .map(|v| self.deserialize_event(v).map_err(|e| e.into())) From 5d6c4852b1c2c1e7fc85dfee219487cd496f56b5 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 19:37:43 +0200 Subject: [PATCH 13/32] fix media content test --- .../src/store/integration_tests.rs | 26 +++++++++---------- .../matrix-sdk-indexeddb/src/state_store.rs | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index 8e414fb18..f0cc51366 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -468,24 +468,24 @@ macro_rules! statestore_integration_tests { }), }; - assert!(store.get_media_content(&request_file).await.unwrap().is_none()); - assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_none()); + assert!(store.get_media_content(&request_file).await.unwrap().is_none(), "unexpectd media found"); + assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_none(), "media not found"); - store.add_media_content(&request_file, content.clone()).await.unwrap(); - assert!(store.get_media_content(&request_file).await.unwrap().is_some()); + store.add_media_content(&request_file, content.clone()).await.expect("adding media failed"); + assert!(store.get_media_content(&request_file).await.unwrap().is_some(), "media not found though added"); - store.remove_media_content(&request_file).await.unwrap(); - assert!(store.get_media_content(&request_file).await.unwrap().is_none()); + store.remove_media_content(&request_file).await.expect("removing media failed"); + assert!(store.get_media_content(&request_file).await.unwrap().is_none(), "media still there after removing"); - store.add_media_content(&request_file, content.clone()).await.unwrap(); - assert!(store.get_media_content(&request_file).await.unwrap().is_some()); + store.add_media_content(&request_file, content.clone()).await.expect("adding media again failed"); + assert!(store.get_media_content(&request_file).await.unwrap().is_some(), "media not found after adding again"); - store.add_media_content(&request_thumbnail, content.clone()).await.unwrap(); - assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_some()); + store.add_media_content(&request_thumbnail, content.clone()).await.expect("adding thumbnail failed"); + assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_some(), "thumbnail not found"); - store.remove_media_content_for_uri(uri).await.unwrap(); - assert!(store.get_media_content(&request_file).await.unwrap().is_none()); - assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_none()); + store.remove_media_content_for_uri(uri).await.expect("removing all media for uri failed"); + assert!(store.get_media_content(&request_file).await.unwrap().is_none(), "media wasn't removed"); + assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_none(), "thumbnail wasn't removed"); } #[async_test] diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 09cd6980e..07664c2e4 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -946,7 +946,7 @@ impl IndexeddbStore { } async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { - let key = self.encode_key(KEYS::MEDIA, request.format.unique_key()); + let key = self.encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); let tx = self.inner.transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readwrite)?; From 24266fe2545bcec9930c14c97ddbb071aaac3dd9 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 19:59:20 +0200 Subject: [PATCH 14/32] fixing ranges --- crates/matrix-sdk-indexeddb/src/safe_encode.rs | 1 - crates/matrix-sdk-indexeddb/src/state_store.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/src/safe_encode.rs b/crates/matrix-sdk-indexeddb/src/safe_encode.rs index 18f1bbe04..c8837c908 100644 --- a/crates/matrix-sdk-indexeddb/src/safe_encode.rs +++ b/crates/matrix-sdk-indexeddb/src/safe_encode.rs @@ -97,7 +97,6 @@ where store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), STANDARD_NO_PAD ), - KEY_SEPARATOR ].concat() } } diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 07664c2e4..62c855625 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -751,7 +751,7 @@ impl IndexeddbStore { self.inner .transaction_on_one_with_mode(KEYS::ROOM_STATE, IdbTransactionMode::Readonly)? .object_store(KEYS::ROOM_STATE)? - .get(&self.encode_key(KEYS::ROOM_STATE, (room_id, &event_type, state_key)))? + .get(&self.encode_key(KEYS::ROOM_STATE, (room_id, event_type, state_key)))? .await? .map(|f| self.deserialize_event(f)) .transpose() @@ -762,7 +762,7 @@ impl IndexeddbStore { room_id: &RoomId, event_type: StateEventType, ) -> Result>> { - let range = self.encode_to_range(KEYS::ROOM_STATE, (room_id, &event_type))?; + let range = self.encode_to_range(KEYS::ROOM_STATE, (room_id, event_type))?; Ok(self .inner .transaction_on_one_with_mode(KEYS::ROOM_STATE, IdbTransactionMode::Readonly)? From 897bb5aed2ee079f62b4ac62e18b2d44e9ddc782 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 12 Apr 2022 20:00:11 +0200 Subject: [PATCH 15/32] new db for every encrypted test --- crates/matrix-sdk-indexeddb/Cargo.toml | 1 + crates/matrix-sdk-indexeddb/src/state_store.rs | 15 ++++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml index 0e5fb1921..80b9e7eae 100644 --- a/crates/matrix-sdk-indexeddb/Cargo.toml +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -38,3 +38,4 @@ matrix-sdk-base = { path = "../matrix-sdk-base", features = ["testing"] } matrix-sdk-crypto = { path = "../matrix-sdk-crypto", features = ["testing"] } matrix-sdk-test = { path = "../matrix-sdk-test" } wasm-bindgen-test = "0.3.24" +uuid = "0.8" \ No newline at end of file diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 62c855625..50301acda 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -199,17 +199,11 @@ impl IndexeddbStore { Ok(IndexeddbStore::open_helper("state".to_owned(), None).await?) } - #[allow(dead_code)] - pub(crate) async fn open_encrypted() -> StoreResult { - let key = StoreCipher::new().map_err::(|e| e.into())?; - Ok(IndexeddbStore::open_helper("state_encrypted".to_owned(), Some(key)).await?) - } - pub async fn open_with_passphrase(name: String, passphrase: &str) -> StoreResult { Ok(Self::inner_open_with_passphrase(name, passphrase).await?) } - async fn inner_open_with_passphrase(name: String, passphrase: &str) -> Result { + pub(crate) async fn inner_open_with_passphrase(name: String, passphrase: &str) -> Result { let name = format!("{:0}::matrix-sdk-state", name); let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&name, 1)?; @@ -1289,10 +1283,13 @@ mod encrypted_tests { use matrix_sdk_base::statestore_integration_tests; - use super::{IndexeddbStore, Result}; + use super::{IndexeddbStore, Result, StoreCipher}; + use uuid::Uuid; async fn get_store() -> Result { - Ok(IndexeddbStore::open_encrypted().await?) + let db_name = format!("test-state-encrypted-{}", Uuid::new_v4().to_hyphenated().to_string()); + let key = StoreCipher::new()?; + Ok(IndexeddbStore::open_helper(db_name, Some(key)).await?) } statestore_integration_tests! { integration } From 262bee51b17821e30b8631f254d2c63e90421ba5 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Wed, 13 Apr 2022 15:23:14 +0200 Subject: [PATCH 16/32] also implement room timeline in encrypted fashion on sled store --- crates/matrix-sdk-sled/src/state_store.rs | 33 ++++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 30f2143e0..746c2bcd0 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -134,12 +134,6 @@ const ROOM_EVENT_ID_POSITION: &str = "room-event-id-to-position"; type Result = std::result::Result; -pub fn encode_key_with_usize>(s: A, i: usize) -> Vec { - // FIXME: Not portable across architectures - [s.as_ref().as_bytes(), &[ENCODE_SEPARATOR], i.to_be_bytes().as_ref(), &[ENCODE_SEPARATOR]] - .concat() -} - #[derive(Clone)] pub struct SledStore { path: Option, @@ -326,9 +320,22 @@ impl SledStore { } } + fn encode_key_with_usize( + &self, + tablename: &str, + s: &A, + i: usize + ) -> Vec { + [ + &self.encode_key(tablename, s), + [ENCODE_SEPARATOR].as_slice(), + i.to_be_bytes().as_ref(), + [ENCODE_SEPARATOR].as_slice(), + ].concat() + } + pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { self.session.insert(self.encode_key(SESSION, &("filter", filter_name)), filter_id)?; - Ok(()) } @@ -960,7 +967,7 @@ impl SledStore { info!("Found previously stored timeline for {}, with end token {:?}", r_id, end_token); let stream = stream! { - while let Ok(Some(item)) = db.room_timeline.get(&encode_key_with_usize(&r_id, position)) { + while let Ok(Some(item)) = db.room_timeline.get(&db.encode_key_with_usize(TIMELINE, &r_id, position)) { position += 1; yield db.deserialize_event(&item).map_err(SledStoreError::from).map_err(|e| e.into()); } @@ -1077,7 +1084,7 @@ impl SledStore { }; let room_version = self .room_info - .get(room_id.encode())? + .get(&self.encode_key(ROOM_INFO, room_id))? .map(|r| self.deserialize_event::(&r)) .transpose()? .and_then(|info| info.room_version().cloned()) @@ -1118,22 +1125,22 @@ impl SledStore { } metadata.start_position -= 1; - let key = encode_key_with_usize(room_id, metadata.start_position); + let key = self.encode_key_with_usize(TIMELINE, room_id, metadata.start_position); timeline_batch.insert(key.as_slice(), self.serialize_event(&event)?); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = (room_id, event_id).encode(); + let event_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); event_id_to_position_batch.insert(event_key.as_slice(), key.as_slice()); } } } else { for event in &timeline.events { metadata.end_position += 1; - let key = encode_key_with_usize(room_id, metadata.end_position); + let key = self.encode_key_with_usize(TIMELINE, room_id, metadata.end_position); timeline_batch.insert(key.as_slice(), self.serialize_event(&event)?); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = (room_id, event_id).encode(); + let event_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); event_id_to_position_batch.insert(event_key.as_slice(), key.as_slice()); } } From 70a5517c5cf7ee28eb98a677f900c501aa35ab77 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Wed, 13 Apr 2022 15:23:50 +0200 Subject: [PATCH 17/32] make room timeline test more debuggable --- .../src/store/integration_tests.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index f0cc51366..ee3811ec2 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -568,7 +568,7 @@ macro_rules! statestore_integration_tests { let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost"); // Before the first sync the timeline should be empty - assert!(store.room_timeline(room_id).await.unwrap().is_none()); + assert!(store.room_timeline(room_id).await.expect("failed to read timeline").is_none(), "TL wasn't empty"); // Add sync response let sync = SyncResponse::try_from_http_response( @@ -701,10 +701,17 @@ macro_rules! statestore_integration_tests { let timeline = timeline_iter.collect::>>().await; - assert!(timeline - .into_iter() - .zip(stored_events.iter()) - .all(|(a, b)| a.unwrap().event_id() == b.event_id())); + let expected: Vec> = stored_events.iter().map(|a| a.event_id().expect("event id doesn't exist")).collect(); + let found: Vec> = timeline.iter().map(|a| a.as_ref().expect("object missing").event_id().clone().expect("event id missing")).collect(); + + for (idx, (a, b)) in timeline + .into_iter() + .zip(stored_events.iter()) + .enumerate() + { + assert_eq!(a.expect("not a value").event_id(), b.event_id(), "pos {} not equal - expected: {:#?}, but found {:#?}", idx, expected, found); + + } } } From a390c3b40b33a9318d12136a202d54ecbd9de2bc Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Wed, 13 Apr 2022 15:52:10 +0200 Subject: [PATCH 18/32] more explicit naming --- crates/matrix-sdk-sled/src/state_store.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 746c2bcd0..92d7d57bd 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -320,7 +320,7 @@ impl SledStore { } } - fn encode_key_with_usize( + fn encode_key_with_counter( &self, tablename: &str, s: &A, @@ -967,7 +967,7 @@ impl SledStore { info!("Found previously stored timeline for {}, with end token {:?}", r_id, end_token); let stream = stream! { - while let Ok(Some(item)) = db.room_timeline.get(&db.encode_key_with_usize(TIMELINE, &r_id, position)) { + while let Ok(Some(item)) = db.room_timeline.get(&db.encode_key_with_counter(TIMELINE, &r_id, position)) { position += 1; yield db.deserialize_event(&item).map_err(SledStoreError::from).map_err(|e| e.into()); } @@ -1125,7 +1125,7 @@ impl SledStore { } metadata.start_position -= 1; - let key = self.encode_key_with_usize(TIMELINE, room_id, metadata.start_position); + let key = self.encode_key_with_counter(TIMELINE, room_id, metadata.start_position); timeline_batch.insert(key.as_slice(), self.serialize_event(&event)?); // Only add event with id to the position map if let Some(event_id) = event.event_id() { @@ -1136,7 +1136,7 @@ impl SledStore { } else { for event in &timeline.events { metadata.end_position += 1; - let key = self.encode_key_with_usize(TIMELINE, room_id, metadata.end_position); + let key = self.encode_key_with_counter(TIMELINE, room_id, metadata.end_position); timeline_batch.insert(key.as_slice(), self.serialize_event(&event)?); // Only add event with id to the position map if let Some(event_id) = event.event_id() { From 9698a50ad85bffd221a6655169f55ee319930924 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Wed, 13 Apr 2022 15:52:57 +0200 Subject: [PATCH 19/32] fixing timeline for encrypted indexeddb store --- .../matrix-sdk-indexeddb/src/safe_encode.rs | 29 ++++++++++++- .../matrix-sdk-indexeddb/src/state_store.rs | 41 +++++++++++++------ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/src/safe_encode.rs b/crates/matrix-sdk-indexeddb/src/safe_encode.rs index c8837c908..4a2503639 100644 --- a/crates/matrix-sdk-indexeddb/src/safe_encode.rs +++ b/crates/matrix-sdk-indexeddb/src/safe_encode.rs @@ -41,11 +41,14 @@ pub trait SafeEncode { JsValue::from(self.as_encoded_string()) } - /// Encode self into a Uint8Array for usage as a key + /// encode self into a JsValue, internally using `as_secure_string` + /// to escape the value of self, fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> JsValue { JsValue::from(self.as_secure_string(table_name, store_cipher)) } + /// encode self securely for the given tablename with the given `store_cipher` hash_key, + /// returns the value as a base64 encoded string without any padding. fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String { base64_encode( store_cipher.hash_key(table_name, self.as_encoded_string().as_bytes()), @@ -53,6 +56,26 @@ pub trait SafeEncode { ) } + /// encode self into a JsValue, internally using `as_encoded_string` + /// to escape the value of self, and append the given counter + fn encode_with_counter(&self, i: usize) -> JsValue { + JsValue::from(format!("{}{}{:0000000x}", + self.as_encoded_string(), + KEY_SEPARATOR, + i + )) + } + + /// encode self into a JsValue, internally using `as_secure_string` + /// to escape the value of self, and append the given counter + fn encode_with_counter_secure(&self, table_name: &str, store_cipher: &StoreCipher, i: usize) -> JsValue { + JsValue::from(format!("{}{}{:0000000x}", + self.as_secure_string(table_name, store_cipher), + KEY_SEPARATOR, + i + )) + } + /// Encode self into a IdbKeyRange for searching all keys that are /// prefixed with this key, followed by `KEY_SEPARATOR`. Internally /// uses `as_encoded_string` to ensure the given key is escaped properly. @@ -275,4 +298,8 @@ impl SafeEncode for usize { fn as_encoded_string(&self) -> String { self.to_string() } + + fn as_secure_string(&self, _table_name: &str, _store_cipher: &StoreCipher) -> String { + self.to_string() + } } diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 50301acda..40f6303e1 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -287,6 +287,16 @@ impl IndexeddbStore { } + fn encode_key_with_counter(&self, table_name: &str, key: &T, i: usize) -> JsValue + where T: SafeEncode + { + match self.store_cipher { + Some(ref cipher) => key.encode_with_counter_secure(table_name, cipher, i), + None => key.encode_with_counter(i) + } + } + + pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { let tx = self .inner @@ -593,7 +603,7 @@ impl IndexeddbStore { let mut delete_timeline = false; for event in &timeline.events { if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &event_id)); + let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); if event_id_to_position_store .count_with_key_owned(event_key)? .await? @@ -695,10 +705,10 @@ impl IndexeddbStore { } metadata.start_position -= 1; - let key = self.encode_key(KEYS::ROOM_TIMELINE, (room_id, &metadata.start_position)); + let key = self.encode_key_with_counter(KEYS::ROOM_TIMELINE, room_id, metadata.start_position); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &event_id)); + let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); event_id_to_position_store.put_key_val(&event_key, &key)?; } @@ -707,10 +717,10 @@ impl IndexeddbStore { } else { for event in &timeline.events { metadata.end_position += 1; - let key = self.encode_key(KEYS::ROOM_TIMELINE, (room_id, &metadata.end_position)); + let key = self.encode_key_with_counter(KEYS::ROOM_TIMELINE, room_id, metadata.end_position); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &event_id)); + let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); event_id_to_position_store.put_key_val(&event_key, &key)?; } @@ -1072,13 +1082,20 @@ impl IndexeddbStore { let timeline = tx.object_store(KEYS::ROOM_TIMELINE)?; let metadata = tx.object_store(KEYS::ROOM_TIMELINE_METADATA)?; - let metadata: Option = - metadata.get(&self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id))?.await?.map(|v| v.into_serde()).transpose()?; - if metadata.is_none() { - info!("No timeline for {} was previously stored", room_id); - return Ok(None); - } - let end_token = metadata.and_then(|m| m.end); + let tlm: TimelineMetadata = match metadata + .get(&self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id))? + .await? + .map(|v| v.into_serde()) + .transpose()? + { + Some(tl) => tl, + _ => { + info!("No timeline for {} was previously stored", room_id); + return Ok(None); + } + }; + + let end_token = tlm.end; #[allow(clippy::needless_collect)] let timeline: Vec> = timeline .get_all_with_key(&self.encode_to_range(KEYS::ROOM_TIMELINE, room_id)?)? From 80791860fea475c9e2dae6153b8a31e87c1c8fe8 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 09:23:29 +0200 Subject: [PATCH 20/32] fixing style and typos --- .../src/store/integration_tests.rs | 2 +- .../matrix-sdk-indexeddb/src/safe_encode.rs | 58 +++--- .../matrix-sdk-indexeddb/src/state_store.rs | 162 +++++++++++------ crates/matrix-sdk-sled/src/cryptostore.rs | 1 - crates/matrix-sdk-sled/src/encode_key.rs | 50 ++---- crates/matrix-sdk-sled/src/state_store.rs | 167 +++++++++++++----- crates/matrix-sdk-store-encryption/src/lib.rs | 19 +- 7 files changed, 286 insertions(+), 173 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index ee3811ec2..a885d6a63 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -468,7 +468,7 @@ macro_rules! statestore_integration_tests { }), }; - assert!(store.get_media_content(&request_file).await.unwrap().is_none(), "unexpectd media found"); + assert!(store.get_media_content(&request_file).await.unwrap().is_none(), "unexpected media found"); assert!(store.get_media_content(&request_thumbnail).await.unwrap().is_none(), "media not found"); store.add_media_content(&request_file, content.clone()).await.expect("adding media failed"); diff --git a/crates/matrix-sdk-indexeddb/src/safe_encode.rs b/crates/matrix-sdk-indexeddb/src/safe_encode.rs index 4a2503639..94f33891f 100644 --- a/crates/matrix-sdk-indexeddb/src/safe_encode.rs +++ b/crates/matrix-sdk-indexeddb/src/safe_encode.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +use base64::{encode_config as base64_encode, STANDARD_NO_PAD}; use matrix_sdk_base::ruma::events::{ GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType, }; @@ -8,7 +9,6 @@ use matrix_sdk_common::ruma::{ use matrix_sdk_store_encryption::StoreCipher; use wasm_bindgen::JsValue; use web_sys::IdbKeyRange; -use base64::{STANDARD_NO_PAD, encode_config as base64_encode}; /// Helpers for wasm32/browser environments @@ -47,29 +47,32 @@ pub trait SafeEncode { JsValue::from(self.as_secure_string(table_name, store_cipher)) } - /// encode self securely for the given tablename with the given `store_cipher` hash_key, - /// returns the value as a base64 encoded string without any padding. + /// encode self securely for the given tablename with the given + /// `store_cipher` hash_key, returns the value as a base64 encoded + /// string without any padding. fn as_secure_string(&self, table_name: &str, store_cipher: &StoreCipher) -> String { base64_encode( store_cipher.hash_key(table_name, self.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ) } /// encode self into a JsValue, internally using `as_encoded_string` /// to escape the value of self, and append the given counter fn encode_with_counter(&self, i: usize) -> JsValue { - JsValue::from(format!("{}{}{:0000000x}", - self.as_encoded_string(), - KEY_SEPARATOR, - i - )) + JsValue::from(format!("{}{}{:0000000x}", self.as_encoded_string(), KEY_SEPARATOR, i)) } /// encode self into a JsValue, internally using `as_secure_string` /// to escape the value of self, and append the given counter - fn encode_with_counter_secure(&self, table_name: &str, store_cipher: &StoreCipher, i: usize) -> JsValue { - JsValue::from(format!("{}{}{:0000000x}", + fn encode_with_counter_secure( + &self, + table_name: &str, + store_cipher: &StoreCipher, + i: usize, + ) -> JsValue { + JsValue::from(format!( + "{}{}{:0000000x}", self.as_secure_string(table_name, store_cipher), KEY_SEPARATOR, i @@ -88,7 +91,11 @@ pub trait SafeEncode { .map_err(|e| e.as_string().unwrap_or_else(|| "Creating key range failed".to_owned())) } - fn encode_to_range_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Result { + fn encode_to_range_secure( + &self, + table_name: &str, + store_cipher: &StoreCipher, + ) -> Result { let key = self.as_secure_string(table_name, store_cipher); IdbKeyRange::bound( &JsValue::from([&key, KEY_SEPARATOR].concat()), @@ -113,14 +120,15 @@ where [ &base64_encode( store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), KEY_SEPARATOR, &base64_encode( store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), - ].concat() + ] + .concat() } } @@ -147,19 +155,20 @@ where [ &base64_encode( store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), KEY_SEPARATOR, &base64_encode( store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), KEY_SEPARATOR, &base64_encode( store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), - ].concat() + ] + .concat() } } @@ -189,24 +198,25 @@ where [ &base64_encode( store_cipher.hash_key(table_name, self.0.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), KEY_SEPARATOR, &base64_encode( store_cipher.hash_key(table_name, self.1.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), KEY_SEPARATOR, &base64_encode( store_cipher.hash_key(table_name, self.2.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), KEY_SEPARATOR, &base64_encode( store_cipher.hash_key(table_name, self.3.as_encoded_string().as_bytes()), - STANDARD_NO_PAD + STANDARD_NO_PAD, ), - ].concat() + ] + .concat() } } diff --git a/crates/matrix-sdk-indexeddb/src/state_store.rs b/crates/matrix-sdk-indexeddb/src/state_store.rs index 40f6303e1..f6e64e647 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store.rs @@ -20,13 +20,9 @@ use indexed_db_futures::prelude::*; use matrix_sdk_base::{ deserialized_responses::{MemberEvent, SyncRoomEvent}, media::{MediaRequest, UniqueKey}, - store::{ - BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError, - }, + store::{BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError}, RoomInfo, }; -use matrix_sdk_store_encryption::{StoreCipher, Error as EncryptionError}; - use matrix_sdk_common::{ async_trait, ruma::{ @@ -44,6 +40,7 @@ use matrix_sdk_common::{ EventId, MxcUri, RoomId, RoomVersionId, UserId, }, }; +use matrix_sdk_store_encryption::{Error as EncryptionError, StoreCipher}; use serde::{Deserialize, Serialize}; use tracing::{info, warn}; use wasm_bindgen::JsValue; @@ -85,8 +82,14 @@ impl From for StoreError { EncryptionError::Random(e) => StoreError::Encryption(e.to_string()), EncryptionError::Serialization(e) => StoreError::Json(e), EncryptionError::Encryption(e) => StoreError::Encryption(e.to_string()), - EncryptionError::Version(found, expected) => StoreError::Encryption(format!("Bad Database Encryption Version: expected {} found {}", expected, found)), - EncryptionError::Length(found, expected) => StoreError::Encryption(format!("The database key an invalid length: expected {} found {}", expected, found)), + EncryptionError::Version(found, expected) => StoreError::Encryption(format!( + "Bad Database Encryption Version: expected {} found {}", + expected, found + )), + EncryptionError::Length(found, expected) => StoreError::Encryption(format!( + "The database key an invalid length: expected {} found {}", + expected, found + )), }, _ => StoreError::Backend(anyhow!(e)), } @@ -231,12 +234,12 @@ impl IndexeddbStore { { StoreCipher::import(passphrase, &inner)? } else { - let ciph = StoreCipher::new()?; + let cipher = StoreCipher::new()?; ob.put_key_val( &JsValue::from_str(KEYS::STORE_KEY), - &JsValue::from_serde(&StoreKeyWrapper(ciph.export(passphrase)?))?, + &JsValue::from_serde(&StoreKeyWrapper(cipher.export(passphrase)?))?, )?; - ciph + cipher }; tx.await.into_result()?; @@ -269,7 +272,8 @@ impl IndexeddbStore { } fn encode_key(&self, table_name: &str, key: T) -> JsValue - where T: SafeEncode + where + T: SafeEncode, { match self.store_cipher { Some(ref cipher) => key.encode_secure(table_name, cipher), @@ -277,26 +281,31 @@ impl IndexeddbStore { } } - fn encode_to_range(&self, table_name: &str, key: T) -> Result - where T: SafeEncode + fn encode_to_range( + &self, + table_name: &str, + key: T, + ) -> Result + where + T: SafeEncode, { match self.store_cipher { Some(ref cipher) => key.encode_to_range_secure(table_name, cipher), - None => key.encode_to_range() - }.map_err(|e| SerializationError::StoreError(StoreError::Backend(anyhow!(e)))) + None => key.encode_to_range(), + } + .map_err(|e| SerializationError::StoreError(StoreError::Backend(anyhow!(e)))) } - fn encode_key_with_counter(&self, table_name: &str, key: &T, i: usize) -> JsValue - where T: SafeEncode + where + T: SafeEncode, { match self.store_cipher { Some(ref cipher) => key.encode_with_counter_secure(table_name, cipher, i), - None => key.encode_with_counter(i) + None => key.encode_with_counter(i), } } - pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { let tx = self .inner @@ -304,7 +313,10 @@ impl IndexeddbStore { let obj = tx.object_store(KEYS::SESSION)?; - obj.put_key_val(&self.encode_key(KEYS::FILTER, (KEYS::FILTER, filter_name)), &JsValue::from_str(filter_id))?; + obj.put_key_val( + &self.encode_key(KEYS::FILTER, (KEYS::FILTER, filter_name)), + &JsValue::from_str(filter_id), + )?; tx.await.into_result()?; @@ -401,7 +413,8 @@ impl IndexeddbStore { for (event_type, event) in &changes.account_data { store.put_key_val( &self.encode_key(KEYS::ACCOUNT_DATA, event_type), - &self.serialize_event(&event)?)?; + &self.serialize_event(&event)?, + )?; } } @@ -431,8 +444,9 @@ impl IndexeddbStore { let store = tx.object_store(KEYS::ROOM_INFOS)?; for (room_id, room_info) in &changes.room_infos { store.put_key_val( - &self.encode_key(KEYS::ROOM_INFOS, room_id), - &self.serialize_event(&room_info)?)?; + &self.encode_key(KEYS::ROOM_INFOS, room_id), + &self.serialize_event(&room_info)?, + )?; } } @@ -441,7 +455,7 @@ impl IndexeddbStore { for (sender, event) in &changes.presence { store.put_key_val( &self.encode_key(KEYS::PRESENCE, sender), - &self.serialize_event(&event)? + &self.serialize_event(&event)?, )?; } } @@ -451,7 +465,7 @@ impl IndexeddbStore { for (room_id, info) in &changes.stripped_room_infos { store.put_key_val( &self.encode_key(KEYS::STRIPPED_ROOM_INFOS, room_id), - &self.serialize_event(&info)? + &self.serialize_event(&info)?, )?; } } @@ -471,7 +485,8 @@ impl IndexeddbStore { for (room, event_types) in &changes.stripped_state { for (event_type, events) in event_types { for (state_key, event) in events { - let key = self.encode_key(KEYS::STRIPPED_ROOM_STATE, (room, event_type, state_key)); + let key = self + .encode_key(KEYS::STRIPPED_ROOM_STATE, (room, event_type, state_key)); store.put_key_val(&key, &self.serialize_event(&event)?)?; } } @@ -494,14 +509,14 @@ impl IndexeddbStore { MembershipState::Join => { joined.put_key_val_owned( &self.encode_key(KEYS::JOINED_USER_IDS, key), - &self.serialize_event(&event.state_key)? + &self.serialize_event(&event.state_key)?, )?; invited.delete(&self.encode_key(KEYS::INVITED_USER_IDS, key))?; } MembershipState::Invite => { invited.put_key_val_owned( &self.encode_key(KEYS::INVITED_USER_IDS, key), - &self.serialize_event(&event.state_key)? + &self.serialize_event(&event.state_key)?, )?; joined.delete(&self.encode_key(KEYS::JOINED_USER_IDS, key))?; } @@ -511,10 +526,16 @@ impl IndexeddbStore { } } - members.put_key_val_owned(&self.encode_key(KEYS::MEMBERS, key), &self.serialize_event(&event)?)?; + members.put_key_val_owned( + &self.encode_key(KEYS::MEMBERS, key), + &self.serialize_event(&event)?, + )?; if let Some(profile) = profile_changes.and_then(|p| p.get(&event.state_key)) { - profiles_store.put_key_val_owned(&self.encode_key(KEYS::PROFILES, key), &self.serialize_event(&profile)?)?; + profiles_store.put_key_val_owned( + &self.encode_key(KEYS::PROFILES, key), + &self.serialize_event(&profile)?, + )?; } } } @@ -528,15 +549,20 @@ impl IndexeddbStore { for (event_id, receipts) in &content.0 { for (receipt_type, receipts) in receipts { for (user_id, receipt) in receipts { - let key = self.encode_key(KEYS::ROOM_USER_RECEIPTS, (room, receipt_type, user_id)); + let key = self.encode_key( + KEYS::ROOM_USER_RECEIPTS, + (room, receipt_type, user_id), + ); if let Some((old_event, _)) = room_user_receipts.get(&key)?.await?.and_then(|f| { self.deserialize_event::<(Box, Receipt)>(f).ok() }) { - room_event_receipts - .delete(&self.encode_key(KEYS::ROOM_EVENT_RECEIPTS, (room, receipt_type, &old_event, user_id)))?; + room_event_receipts.delete(&self.encode_key( + KEYS::ROOM_EVENT_RECEIPTS, + (room, receipt_type, &old_event, user_id), + ))?; } room_user_receipts @@ -544,7 +570,10 @@ impl IndexeddbStore { // Add the receipt to the room event receipts room_event_receipts.put_key_val( - &self.encode_key(KEYS::ROOM_EVENT_RECEIPTS, (room, receipt_type, event_id, user_id)), + &self.encode_key( + KEYS::ROOM_EVENT_RECEIPTS, + (room, receipt_type, event_id, user_id), + ), &self.serialize_event(&(user_id, receipt))?, )?; } @@ -574,7 +603,7 @@ impl IndexeddbStore { let stores = &[ (KEYS::ROOM_TIMELINE, &timeline_store), (KEYS::ROOM_TIMELINE_METADATA, &timeline_metadata_store), - (KEYS::ROOM_EVENT_ID_TO_POSITION, &event_id_to_position_store) + (KEYS::ROOM_EVENT_ID_TO_POSITION, &event_id_to_position_store), ]; for (table_name, store) in stores { let range = self.encode_to_range(table_name, room_id)?; @@ -603,7 +632,10 @@ impl IndexeddbStore { let mut delete_timeline = false; for event in &timeline.events { if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); + let event_key = self.encode_key( + KEYS::ROOM_EVENT_ID_TO_POSITION, + (room_id, event_id), + ); if event_id_to_position_store .count_with_key_owned(event_key)? .await? @@ -621,11 +653,10 @@ impl IndexeddbStore { room_id ); - let stores = &[ (KEYS::ROOM_TIMELINE, &timeline_store), (KEYS::ROOM_TIMELINE_METADATA, &timeline_metadata_store), - (KEYS::ROOM_EVENT_ID_TO_POSITION, &event_id_to_position_store) + (KEYS::ROOM_EVENT_ID_TO_POSITION, &event_id_to_position_store), ]; for (table_name, store) in stores { let range = self.encode_to_range(table_name, room_id)?; @@ -678,7 +709,10 @@ impl IndexeddbStore { AnySyncMessageLikeEvent::RoomRedaction(redaction), )) = event.event.deserialize() { - let redacts_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, &redaction.redacts)); + let redacts_key = self.encode_key( + KEYS::ROOM_EVENT_ID_TO_POSITION, + (room_id, &redaction.redacts), + ); if let Some(position_key) = event_id_to_position_store.get_owned(redacts_key)?.await? { @@ -705,10 +739,15 @@ impl IndexeddbStore { } metadata.start_position -= 1; - let key = self.encode_key_with_counter(KEYS::ROOM_TIMELINE, room_id, metadata.start_position); + let key = self.encode_key_with_counter( + KEYS::ROOM_TIMELINE, + room_id, + metadata.start_position, + ); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); + let event_key = self + .encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); event_id_to_position_store.put_key_val(&event_key, &key)?; } @@ -717,10 +756,15 @@ impl IndexeddbStore { } else { for event in &timeline.events { metadata.end_position += 1; - let key = self.encode_key_with_counter(KEYS::ROOM_TIMELINE, room_id, metadata.end_position); + let key = self.encode_key_with_counter( + KEYS::ROOM_TIMELINE, + room_id, + metadata.end_position, + ); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); + let event_key = self + .encode_key(KEYS::ROOM_EVENT_ID_TO_POSITION, (room_id, event_id)); event_id_to_position_store.put_key_val(&event_key, &key)?; } @@ -728,8 +772,10 @@ impl IndexeddbStore { } } - timeline_metadata_store - .put_key_val_owned(&self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id), &JsValue::from_serde(&metadata)?)?; + timeline_metadata_store.put_key_val_owned( + &self.encode_key(KEYS::ROOM_TIMELINE_METADATA, room_id), + &JsValue::from_serde(&metadata)?, + )?; } } @@ -807,10 +853,8 @@ impl IndexeddbStore { } pub async fn get_user_ids_stream(&self, room_id: &RoomId) -> Result>> { - Ok([ - self.get_invited_user_ids(room_id).await?, - self.get_joined_user_ids(room_id).await? - ].concat()) + Ok([self.get_invited_user_ids(room_id).await?, self.get_joined_user_ids(room_id).await?] + .concat()) } pub async fn get_invited_user_ids(&self, room_id: &RoomId) -> Result>> { @@ -934,7 +978,8 @@ impl IndexeddbStore { receipt_type: ReceiptType, event_id: &EventId, ) -> Result, Receipt)>> { - let range = self.encode_to_range(KEYS::ROOM_EVENT_RECEIPTS, (room_id, &receipt_type, event_id))?; + let range = + self.encode_to_range(KEYS::ROOM_EVENT_RECEIPTS, (room_id, &receipt_type, event_id))?; let tx = self.inner.transaction_on_one_with_mode( KEYS::ROOM_EVENT_RECEIPTS, IdbTransactionMode::Readonly, @@ -950,7 +995,8 @@ impl IndexeddbStore { } async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { - let key = self.encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); + let key = self + .encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); let tx = self.inner.transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readwrite)?; @@ -960,7 +1006,8 @@ impl IndexeddbStore { } async fn get_media_content(&self, request: &MediaRequest) -> Result>> { - let key = self.encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); + let key = self + .encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); self.inner .transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readonly)? .object_store(KEYS::MEDIA)? @@ -1004,7 +1051,8 @@ impl IndexeddbStore { } async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> { - let key = self.encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); + let key = self + .encode_key(KEYS::MEDIA, (request.source.unique_key(), request.format.unique_key())); let tx = self.inner.transaction_on_one_with_mode(KEYS::MEDIA, IdbTransactionMode::Readwrite)?; @@ -1299,16 +1347,16 @@ mod encrypted_tests { wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); use matrix_sdk_base::statestore_integration_tests; - - use super::{IndexeddbStore, Result, StoreCipher}; use uuid::Uuid; + use super::{IndexeddbStore, Result, StoreCipher}; + async fn get_store() -> Result { - let db_name = format!("test-state-encrypted-{}", Uuid::new_v4().to_hyphenated().to_string()); + let db_name = + format!("test-state-encrypted-{}", Uuid::new_v4().to_hyphenated().to_string()); let key = StoreCipher::new()?; Ok(IndexeddbStore::open_helper(db_name, Some(key)).await?) } statestore_integration_tests! { integration } } - diff --git a/crates/matrix-sdk-sled/src/cryptostore.rs b/crates/matrix-sdk-sled/src/cryptostore.rs index c8afd2fd8..2c30c638a 100644 --- a/crates/matrix-sdk-sled/src/cryptostore.rs +++ b/crates/matrix-sdk-sled/src/cryptostore.rs @@ -186,7 +186,6 @@ impl EncodeSecureKey for Session { } } - #[derive(Clone, Debug)] pub struct AccountInfo { user_id: Arc, diff --git a/crates/matrix-sdk-sled/src/encode_key.rs b/crates/matrix-sdk-sled/src/encode_key.rs index 097c4dc0b..8ae46ea40 100644 --- a/crates/matrix-sdk-sled/src/encode_key.rs +++ b/crates/matrix-sdk-sled/src/encode_key.rs @@ -1,12 +1,13 @@ use matrix_sdk_common::ruma::{ - events::{secret::request::SecretName, GlobalAccountDataEventType, RoomAccountDataEventType, StateEventType}, + events::{ + secret::request::SecretName, GlobalAccountDataEventType, RoomAccountDataEventType, + StateEventType, + }, receipt::ReceiptType, - EventEncryptionAlgorithm, - DeviceId, EventId, MxcUri, RoomId, TransactionId, UserId, + DeviceId, EventEncryptionAlgorithm, EventId, MxcUri, RoomId, TransactionId, UserId, }; use matrix_sdk_store_encryption::StoreCipher; - pub const ENCODE_SEPARATOR: u8 = 0xff; pub trait EncodeKey { @@ -107,11 +108,7 @@ where B: EncodeKey, { fn encode(&self) -> Vec { - [ - self.0.encode(), - self.1.encode(), - ] - .concat() + [self.0.encode(), self.1.encode()].concat() } } @@ -122,12 +119,7 @@ where C: EncodeKey, { fn encode(&self) -> Vec { - [ - self.0.encode(), - self.1.encode(), - self.2.encode(), - ] - .concat() + [self.0.encode(), self.1.encode(), self.2.encode()].concat() } } @@ -139,13 +131,7 @@ where D: EncodeKey, { fn encode(&self) -> Vec { - [ - self.0.encode(), - self.1.encode(), - self.2.encode(), - self.3.encode(), - ] - .concat() + [self.0.encode(), self.1.encode(), self.2.encode(), self.3.encode()].concat() } } @@ -161,12 +147,10 @@ impl EncodeKey for GlobalAccountDataEventType { } } - pub trait EncodeSecureKey { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec; } - impl EncodeSecureKey for &T { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { T::encode_secure(self, table_name, store_cipher) @@ -179,7 +163,6 @@ impl EncodeSecureKey for Box { } } - impl EncodeSecureKey for UserId { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { let user_id = store_cipher.hash_key(table_name, self.as_bytes()); @@ -221,10 +204,7 @@ impl EncodeSecureKey for EventId { impl EncodeSecureKey for MxcUri { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { let s: &str = self.as_ref(); - [ - store_cipher.hash_key(table_name, s.as_bytes()).as_slice(), - &[ENCODE_SEPARATOR], - ].concat() + [store_cipher.hash_key(table_name, s.as_bytes()).as_slice(), &[ENCODE_SEPARATOR]].concat() } } @@ -241,7 +221,8 @@ impl EncodeSecureKey for GlobalAccountDataEventType { [ store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), &[ENCODE_SEPARATOR], - ].concat() + ] + .concat() } } @@ -250,7 +231,8 @@ impl EncodeSecureKey for StateEventType { [ store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), &[ENCODE_SEPARATOR], - ].concat() + ] + .concat() } } @@ -259,11 +241,11 @@ impl EncodeSecureKey for RoomAccountDataEventType { [ store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), &[ENCODE_SEPARATOR], - ].concat() + ] + .concat() } } - impl EncodeSecureKey for (A, B) where A: EncodeSecureKey, @@ -310,4 +292,4 @@ where ] .concat() } -} \ No newline at end of file +} diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 92d7d57bd..2dc161e65 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -23,13 +23,10 @@ use anyhow::anyhow; use async_stream::stream; use futures_core::stream::Stream; use futures_util::stream::{self, StreamExt, TryStreamExt}; -use matrix_sdk_store_encryption::{StoreCipher, Error as KeyEncryptionError}; use matrix_sdk_base::{ deserialized_responses::{MemberEvent, SyncRoomEvent}, media::{MediaRequest, UniqueKey}, - store::{ - BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError, - }, + store::{BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError}, RoomInfo, }; use matrix_sdk_common::{ @@ -50,6 +47,7 @@ use matrix_sdk_common::{ EventId, MxcUri, RoomId, RoomVersionId, UserId, }, }; +use matrix_sdk_store_encryption::{Error as KeyEncryptionError, StoreCipher}; use serde::{Deserialize, Serialize}; use sled::{ transaction::{ConflictableTransactionError, TransactionError}, @@ -60,7 +58,7 @@ use tracing::{info, warn}; #[cfg(feature = "crypto-store")] use super::OpenStoreError; -use crate::encode_key::{EncodeSecureKey, EncodeKey, ENCODE_SEPARATOR}; +use crate::encode_key::{EncodeKey, EncodeSecureKey, ENCODE_SEPARATOR}; #[cfg(feature = "crypto-store")] pub use crate::CryptoStore; @@ -99,8 +97,14 @@ impl Into for SledStoreError { KeyEncryptionError::Random(e) => StoreError::Encryption(e.to_string()), KeyEncryptionError::Serialization(e) => StoreError::Json(e), KeyEncryptionError::Encryption(e) => StoreError::Encryption(e.to_string()), - KeyEncryptionError::Version(found, expected) => StoreError::Encryption(format!("Bad Database Encryption Version: expected {} found {}", expected, found)), - KeyEncryptionError::Length(found, expected) => StoreError::Encryption(format!("The database key an invalid length: expected {} found {}", expected, found)), + KeyEncryptionError::Version(found, expected) => StoreError::Encryption(format!( + "Bad Database Encryption Version: expected {} found {}", + expected, found + )), + KeyEncryptionError::Length(found, expected) => StoreError::Encryption(format!( + "The database key an invalid length: expected {} found {}", + expected, found + )), }, SledStoreError::StoreError(e) => e, _ => StoreError::Backend(anyhow!(self)), @@ -108,7 +112,6 @@ impl Into for SledStoreError { } } - const SESSION: &str = "session"; const ACCOUNT_DATA: &str = "account-data"; const MEMBER: &str = "member"; @@ -173,7 +176,11 @@ impl std::fmt::Debug for SledStore { } impl SledStore { - fn open_helper(db: Db, path: Option, store_cipher: Option) -> Result { + fn open_helper( + db: Db, + path: Option, + store_cipher: Option, + ) -> Result { let session = db.open_tree(SESSION)?; let account_data = db.open_tree(ACCOUNT_DATA)?; @@ -244,10 +251,14 @@ impl SledStore { let db = Config::new().temporary(true).open().map_err(|e| StoreError::Backend(anyhow!(e)))?; - SledStore::open_helper(db, None, Some(StoreCipher::new().expect("can't create store cipher"))).map_err(|e| e.into()) + SledStore::open_helper( + db, + None, + Some(StoreCipher::new().expect("can't create store cipher")), + ) + .map_err(|e| e.into()) } - pub fn open_with_passphrase(path: impl AsRef, passphrase: &str) -> StoreResult { Self::inner_open_with_passphrase(path, passphrase).map_err(|e| e.into()) } @@ -259,9 +270,9 @@ impl SledStore { let store_cipher = if let Some(inner) = db.get("store_cipher".encode())? { StoreCipher::import(passphrase, &inner)? } else { - let ciph = StoreCipher::new()?; - db.insert("store_cipher".encode(), ciph.export(passphrase)?)?; - ciph + let cipher = StoreCipher::new()?; + db.insert("store_cipher".encode(), cipher.export(passphrase)?)?; + cipher }; SledStore::open_helper(db, Some(path), Some(store_cipher)) @@ -307,7 +318,7 @@ impl SledStore { Ok(serde_json::from_slice(event)?) } } - + fn encode_key( &self, table_name: &str, @@ -324,14 +335,15 @@ impl SledStore { &self, tablename: &str, s: &A, - i: usize + i: usize, ) -> Vec { [ &self.encode_key(tablename, s), [ENCODE_SEPARATOR].as_slice(), i.to_be_bytes().as_ref(), [ENCODE_SEPARATOR].as_slice(), - ].concat() + ] + .concat() } pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { @@ -401,11 +413,17 @@ impl SledStore { match event.content.membership { MembershipState::Join => { - joined.insert(self.encode_key(JOINED_USER_ID, &key), event.state_key.as_str())?; + joined.insert( + self.encode_key(JOINED_USER_ID, &key), + event.state_key.as_str(), + )?; invited.remove(self.encode_key(INVITED_USER_ID, &key))?; } MembershipState::Invite => { - invited.insert(self.encode_key(INVITED_USER_ID, &key), event.state_key.as_str())?; + invited.insert( + self.encode_key(INVITED_USER_ID, &key), + event.state_key.as_str(), + )?; joined.remove(self.encode_key(JOINED_USER_ID, &key))?; } _ => { @@ -499,7 +517,10 @@ impl SledStore { for (room, events) in &changes.stripped_members { for event in events.values() { stripped_members.insert( - self.encode_key(STRIPPED_ROOM_MEMBER, &(room, event.state_key.to_string())), + self.encode_key( + STRIPPED_ROOM_MEMBER, + &(room, event.state_key.to_string()), + ), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -510,7 +531,10 @@ impl SledStore { for (event_type, events) in event_types { for (state_key, event) in events { stripped_state.insert( - self.encode_key(STRIPPED_ROOM_STATE, &(room, event_type.to_string(), state_key)), + self.encode_key( + STRIPPED_ROOM_STATE, + &(room, event_type.to_string(), state_key), + ), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -533,7 +557,10 @@ impl SledStore { for (user_id, receipt) in receipts { // Add the receipt to the room user receipts if let Some(old) = room_user_receipts.insert( - self.encode_key(ROOM_USER_RECEIPT, &(room, receipt_type, user_id)), + self.encode_key( + ROOM_USER_RECEIPT, + &(room, receipt_type, user_id), + ), self.serialize_event(&(event_id, receipt)) .map_err(ConflictableTransactionError::Abort)?, )? { @@ -541,14 +568,18 @@ impl SledStore { let (old_event, _): (Box, Receipt) = self .deserialize_event(&old) .map_err(ConflictableTransactionError::Abort)?; - room_event_receipts.remove( - self.encode_key(ROOM_EVENT_RECEIPT, &(room, receipt_type, old_event, user_id)), - )?; + room_event_receipts.remove(self.encode_key( + ROOM_EVENT_RECEIPT, + &(room, receipt_type, old_event, user_id), + ))?; } // Add the receipt to the room event receipts room_event_receipts.insert( - self.encode_key(ROOM_EVENT_RECEIPT, &(room, receipt_type, event_id, user_id)), + self.encode_key( + ROOM_EVENT_RECEIPT, + &(room, receipt_type, event_id, user_id), + ), self.serialize_event(&(user_id, receipt)) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -635,7 +666,10 @@ impl SledStore { &self, room_id: &RoomId, ) -> StoreResult>>> { - Ok(self.get_joined_user_ids(room_id).await?.chain(self.get_invited_user_ids(room_id).await?)) + Ok(self + .get_joined_user_ids(room_id) + .await? + .chain(self.get_invited_user_ids(room_id).await?)) } pub async fn get_invited_user_ids( @@ -780,8 +814,10 @@ impl SledStore { } async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { - self.media - .insert(self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())), data)?; + self.media.insert( + self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())), + data, + )?; self.inner.flush_async().await?; @@ -790,7 +826,8 @@ impl SledStore { async fn get_media_content(&self, request: &MediaRequest) -> Result>> { let db = self.clone(); - let key = self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())); + let key = + self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())); spawn_blocking(move || Ok(db.media.get(key)?.map(|m| m.to_vec()))).await? } @@ -809,7 +846,9 @@ impl SledStore { } async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> { - self.media.remove(self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())))?; + self.media.remove( + self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())), + )?; Ok(()) } @@ -826,8 +865,7 @@ impl SledStore { } async fn remove_room(&self, room_id: &RoomId) -> Result<()> { - - let mut members_batch = sled::Batch::default(); + let mut members_batch = sled::Batch::default(); for key in self.members.scan_prefix(self.encode_key(MEMBER, room_id)).keys() { members_batch.remove(key?) } @@ -843,12 +881,15 @@ impl SledStore { } let mut joined_user_ids_batch = sled::Batch::default(); - for key in self.joined_user_ids.scan_prefix(self.encode_key(JOINED_USER_ID, room_id)).keys() { + for key in self.joined_user_ids.scan_prefix(self.encode_key(JOINED_USER_ID, room_id)).keys() + { joined_user_ids_batch.remove(key?) } let mut invited_user_ids_batch = sled::Batch::default(); - for key in self.invited_user_ids.scan_prefix(self.encode_key(INVITED_USER_ID, room_id)).keys() { + for key in + self.invited_user_ids.scan_prefix(self.encode_key(INVITED_USER_ID, room_id)).keys() + { invited_user_ids_batch.remove(key?) } @@ -858,27 +899,41 @@ impl SledStore { } let mut room_account_data_batch = sled::Batch::default(); - for key in self.room_account_data.scan_prefix(self.encode_key(ROOM_ACCOUNT_DATA, room_id)).keys() { + for key in + self.room_account_data.scan_prefix(self.encode_key(ROOM_ACCOUNT_DATA, room_id)).keys() + { room_account_data_batch.remove(key?) } let mut stripped_members_batch = sled::Batch::default(); - for key in self.stripped_members.scan_prefix(self.encode_key(STRIPPED_ROOM_MEMBER, room_id)).keys() { + for key in + self.stripped_members.scan_prefix(self.encode_key(STRIPPED_ROOM_MEMBER, room_id)).keys() + { stripped_members_batch.remove(key?) } let mut stripped_room_state_batch = sled::Batch::default(); - for key in self.stripped_room_state.scan_prefix(self.encode_key(STRIPPED_ROOM_STATE, room_id)).keys() { + for key in self + .stripped_room_state + .scan_prefix(self.encode_key(STRIPPED_ROOM_STATE, room_id)) + .keys() + { stripped_room_state_batch.remove(key?) } let mut room_user_receipts_batch = sled::Batch::default(); - for key in self.room_user_receipts.scan_prefix(self.encode_key(ROOM_USER_RECEIPT, room_id)).keys() { + for key in + self.room_user_receipts.scan_prefix(self.encode_key(ROOM_USER_RECEIPT, room_id)).keys() + { room_user_receipts_batch.remove(key?) } let mut room_event_receipts_batch = sled::Batch::default(); - for key in self.room_event_receipts.scan_prefix(self.encode_key(ROOM_EVENT_RECEIPT, room_id)).keys() { + for key in self + .room_event_receipts + .scan_prefix(self.encode_key(ROOM_EVENT_RECEIPT, room_id)) + .keys() + { room_event_receipts_batch.remove(key?) } @@ -985,7 +1040,11 @@ impl SledStore { } let mut event_id_to_position_batch = sled::Batch::default(); - for key in self.room_event_id_to_position.scan_prefix(self.encode_key(ROOM_EVENT_ID_POSITION, &room_id)).keys() { + for key in self + .room_event_id_to_position + .scan_prefix(self.encode_key(ROOM_EVENT_ID_POSITION, &room_id)) + .keys() + { event_id_to_position_batch.remove(key?) } @@ -993,7 +1052,8 @@ impl SledStore { (&self.room_timeline, &self.room_timeline_metadata, &self.room_event_id_to_position) .transaction( |(room_timeline, room_timeline_metadata, room_event_id_to_position)| { - room_timeline_metadata.remove(self.encode_key(TIMELINE_METADATA, &room_id))?; + room_timeline_metadata + .remove(self.encode_key(TIMELINE_METADATA, &room_id))?; room_timeline.apply_batch(&timeline_batch)?; room_event_id_to_position.apply_batch(&event_id_to_position_batch)?; @@ -1045,7 +1105,8 @@ impl SledStore { let mut delete_timeline = false; for event in &timeline.events { if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); + let event_key = + self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); if self.room_event_id_to_position.contains_key(event_key)? { delete_timeline = true; break; @@ -1100,7 +1161,8 @@ impl SledStore { AnySyncMessageLikeEvent::RoomRedaction(redaction), )) = event.event.deserialize() { - let redacts_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, redaction.redacts)); + let redacts_key = + self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, redaction.redacts)); if let Some(position_key) = self.room_event_id_to_position.get(redacts_key)? { @@ -1125,28 +1187,35 @@ impl SledStore { } metadata.start_position -= 1; - let key = self.encode_key_with_counter(TIMELINE, room_id, metadata.start_position); + let key = + self.encode_key_with_counter(TIMELINE, room_id, metadata.start_position); timeline_batch.insert(key.as_slice(), self.serialize_event(&event)?); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); + let event_key = + self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); event_id_to_position_batch.insert(event_key.as_slice(), key.as_slice()); } } } else { for event in &timeline.events { metadata.end_position += 1; - let key = self.encode_key_with_counter(TIMELINE, room_id, metadata.end_position); + let key = + self.encode_key_with_counter(TIMELINE, room_id, metadata.end_position); timeline_batch.insert(key.as_slice(), self.serialize_event(&event)?); // Only add event with id to the position map if let Some(event_id) = event.event_id() { - let event_key = self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); + let event_key = + self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); event_id_to_position_batch.insert(event_key.as_slice(), key.as_slice()); } } } - timeline_metadata_batch.insert(self.encode_key(TIMELINE_METADATA, &room_id), serde_json::to_vec(&metadata)?); + timeline_metadata_batch.insert( + self.encode_key(TIMELINE_METADATA, &room_id), + serde_json::to_vec(&metadata)?, + ); } let ret: Result<(), TransactionError> = diff --git a/crates/matrix-sdk-store-encryption/src/lib.rs b/crates/matrix-sdk-store-encryption/src/lib.rs index 1adeb33a3..4ff7b6d4a 100644 --- a/crates/matrix-sdk-store-encryption/src/lib.rs +++ b/crates/matrix-sdk-store-encryption/src/lib.rs @@ -289,8 +289,9 @@ impl StoreCipher { /// Encrypt a value before it is inserted into the key/value store. /// - /// A value can be decrypted using the [`StoreCipher::decrypt_value_typed()`] - /// method. This is the lower level function to `encrypt_value`, but returns the + /// A value can be decrypted using the + /// [`StoreCipher::decrypt_value_typed()`] method. This is the lower + /// level function to `encrypt_value`, but returns the /// full `EncryptdValue`-type /// /// # Arguments @@ -399,8 +400,9 @@ impl StoreCipher { /// Decrypt a value after it was fetchetd from the key/value store. /// - /// A value can be encrypted using the [`StoreCipher::encrypt_value_typed()`] - /// method. Lower level method to [`StoreCipher::decrypt_value_typed()`] + /// A value can be encrypted using the + /// [`StoreCipher::encrypt_value_typed()`] method. Lower level method to + /// [`StoreCipher::decrypt_value_typed()`] /// /// # Arguments /// @@ -427,14 +429,16 @@ impl StoreCipher { /// assert_eq!(value, decrypted); /// # Result::<_, anyhow::Error>::Ok(()) }; /// ``` - pub fn decrypt_value_typed Deserialize<'b>>(&self, value: EncryptedValue) -> Result { + pub fn decrypt_value_typed Deserialize<'b>>( + &self, + value: EncryptedValue, + ) -> Result { let mut plaintext = self.decrypt_value_data(value)?; let ret = serde_json::from_slice(&plaintext); plaintext.zeroize(); Ok(ret?) } - /// Decrypt a value after it was fetchetd from the key/value store. /// /// A value can be encrypted using the [`StoreCipher::encrypt_value_data()`] @@ -494,7 +498,8 @@ impl MacKey { } } -/// Encrypted value, ready for storage, as created by the [`StoreCipher::encrypt_value_data()`] +/// Encrypted value, ready for storage, as created by the +/// [`StoreCipher::encrypt_value_data()`] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct EncryptedValue { version: u8, From 4e8383ea4fc8fffd91ff09c1df43b9bb57bdb19d Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 09:26:28 +0200 Subject: [PATCH 21/32] fixing clippy lints --- crates/matrix-sdk-sled/src/state_store.rs | 4 ++-- crates/matrix-sdk-store-encryption/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 2dc161e65..3d95cc993 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -714,7 +714,7 @@ impl SledStore { let db = self.clone(); spawn_blocking(move || { stream::iter( - db.room_info.iter().map(move |r| db.deserialize_event(&r?.1).map_err(|e| e)), + db.room_info.iter().map(move |r| db.deserialize_event(&r?.1)), ) }) .await @@ -727,7 +727,7 @@ impl SledStore { stream::iter( db.stripped_room_infos .iter() - .map(move |r| db.deserialize_event(&r?.1).map_err(|e| e)), + .map(move |r| db.deserialize_event(&r?.1)), ) }) .await diff --git a/crates/matrix-sdk-store-encryption/src/lib.rs b/crates/matrix-sdk-store-encryption/src/lib.rs index 4ff7b6d4a..68612b1c5 100644 --- a/crates/matrix-sdk-store-encryption/src/lib.rs +++ b/crates/matrix-sdk-store-encryption/src/lib.rs @@ -322,7 +322,7 @@ impl StoreCipher { /// ``` pub fn encrypt_value_typed(&self, value: &impl Serialize) -> Result { let data = serde_json::to_vec(value)?; - Ok(self.encrypt_value_data(data)?) + self.encrypt_value_data(data) } /// Encrypt some data before it is inserted into the key/value store. From 05b3077a7a1b2e72dc8b048e650163ada6fa0a24 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 11:00:06 +0200 Subject: [PATCH 22/32] fixing lints again --- crates/matrix-sdk-sled/src/state_store.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index d30435619..390f0c242 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -27,9 +27,7 @@ use matrix_sdk_base::{ deserialized_responses::{MemberEvent, SyncRoomEvent}, media::{MediaRequest, UniqueKey}, ruma::events::room::redaction::SyncRoomRedactionEvent, - store::{ - BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError, - }, + store::{BoxStream, Result as StoreResult, StateChanges, StateStore, StoreError}, RoomInfo, }; use matrix_sdk_common::{ From 872f32a7e067079b3b30d2974bc219e3a9fe0d0f Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 12:08:26 +0200 Subject: [PATCH 23/32] sorting line order --- crates/matrix-sdk-indexeddb/Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml index 80b9e7eae..297c72b70 100644 --- a/crates/matrix-sdk-indexeddb/Cargo.toml +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -11,22 +11,22 @@ encryption = ["matrix-sdk-base/encryption", "matrix-sdk-crypto"] default-target = "wasm32-unknown-unknown" [dependencies] -matrix-sdk-base = { path = "../matrix-sdk-base" } -matrix-sdk-crypto = { path = "../matrix-sdk-crypto", optional = true } -matrix-sdk-common = { path = "../matrix-sdk-common" } -matrix-sdk-store-encryption = { path = "../matrix-sdk-store-encryption" } -thiserror = "1.0.25" anyhow = "1" - +base64 = { version = "0.13.0" } +dashmap = "5.1.0" futures-util = { version = "0.3.15", default-features = false } indexed_db_futures = { version = "0.2.0" } -wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } -web-sys = { version = "0.3.35", features = ["IdbKeyRange"] } -base64 = { version = "0.13.0" } +matrix-sdk-base = { path = "../matrix-sdk-base" } +matrix-sdk-common = { path = "../matrix-sdk-common" } +matrix-sdk-store-encryption = { path = "../matrix-sdk-store-encryption" } serde = { version = "1.0.126" } serde_json = "1.0.64" -dashmap = "5.1.0" +thiserror = "1.0.25" tracing = "0.1.26" +wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } +web-sys = { version = "0.3.35", features = ["IdbKeyRange"] } + +matrix-sdk-crypto = { path = "../matrix-sdk-crypto", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -37,5 +37,5 @@ getrandom = { version = "0.2", features = ["js"] } matrix-sdk-base = { path = "../matrix-sdk-base", features = ["testing"] } matrix-sdk-crypto = { path = "../matrix-sdk-crypto", features = ["testing"] } matrix-sdk-test = { path = "../matrix-sdk-test" } -wasm-bindgen-test = "0.3.24" -uuid = "0.8" \ No newline at end of file +uuid = "0.8" +wasm-bindgen-test = "0.3.24" \ No newline at end of file From 9d09401ede35b566d596a3e2d682dc7ef614cbb6 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 12:20:41 +0200 Subject: [PATCH 24/32] .into instead of ::from --- crates/matrix-sdk-indexeddb/src/safe_encode.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/src/safe_encode.rs b/crates/matrix-sdk-indexeddb/src/safe_encode.rs index 94f33891f..725c27300 100644 --- a/crates/matrix-sdk-indexeddb/src/safe_encode.rs +++ b/crates/matrix-sdk-indexeddb/src/safe_encode.rs @@ -38,13 +38,13 @@ pub trait SafeEncode { /// encode self into a JsValue, internally using `as_encoded_string` /// to escape the value of self. fn encode(&self) -> JsValue { - JsValue::from(self.as_encoded_string()) + self.as_encoded_string().into() } /// encode self into a JsValue, internally using `as_secure_string` /// to escape the value of self, fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> JsValue { - JsValue::from(self.as_secure_string(table_name, store_cipher)) + self.as_secure_string(table_name, store_cipher).into() } /// encode self securely for the given tablename with the given @@ -60,7 +60,7 @@ pub trait SafeEncode { /// encode self into a JsValue, internally using `as_encoded_string` /// to escape the value of self, and append the given counter fn encode_with_counter(&self, i: usize) -> JsValue { - JsValue::from(format!("{}{}{:0000000x}", self.as_encoded_string(), KEY_SEPARATOR, i)) + format!("{}{}{:0000000x}", self.as_encoded_string(), KEY_SEPARATOR, i).into() } /// encode self into a JsValue, internally using `as_secure_string` @@ -71,12 +71,12 @@ pub trait SafeEncode { store_cipher: &StoreCipher, i: usize, ) -> JsValue { - JsValue::from(format!( + format!( "{}{}{:0000000x}", self.as_secure_string(table_name, store_cipher), KEY_SEPARATOR, i - )) + ).into() } /// Encode self into a IdbKeyRange for searching all keys that are From 953d759238be46cb19581afe7401d4fac8245d00 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 12:21:26 +0200 Subject: [PATCH 25/32] sort and typo --- crates/matrix-sdk-sled/src/state_store.rs | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 390f0c242..42d7d8d3a 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -113,28 +113,28 @@ impl Into for SledStoreError { } } -const SESSION: &str = "session"; const ACCOUNT_DATA: &str = "account-data"; -const MEMBER: &str = "member"; -const PROFILE: &str = "profile"; -const ROOM_STATE: &str = "room-state"; -const PRESENCE: &str = "presence"; -const ROOM: &str = "room"; -const ROOM_INFO: &str = "room-info"; +const CUSTOM: &str = "custom"; const DISPLAY_NAME: &str = "display-name"; +const INVITED_USER_ID: &str = "invited-user-id"; const JOINED_USER_ID: &str = "joined-user-id"; -const INVITED_USER_ID: &str = "invited-user-ids"; +const MEDIA: &str = "media"; +const MEMBER: &str = "member"; +const PRESENCE: &str = "presence"; +const PROFILE: &str = "profile"; const ROOM_ACCOUNT_DATA: &str = "room-account-data"; +const ROOM_EVENT_ID_POSITION: &str = "room-event-id-to-position"; +const ROOM_EVENT_RECEIPT: &str = "room-event-receipt"; +const ROOM_INFO: &str = "room-info"; +const ROOM_STATE: &str = "room-state"; +const ROOM_USER_RECEIPT: &str = "room-user-receipt"; +const ROOM: &str = "room"; +const SESSION: &str = "session"; const STRIPPED_ROOM_INFO: &str = "stripped-room-info"; const STRIPPED_ROOM_MEMBER: &str = "stripped-room-member"; const STRIPPED_ROOM_STATE: &str = "stripped-room-state"; -const ROOM_USER_RECEIPT: &str = "room-user-receipt"; -const ROOM_EVENT_RECEIPT: &str = "room-event-receipt"; -const MEDIA: &str = "media"; -const CUSTOM: &str = "custom"; -const TIMELINE: &str = "timeline"; const TIMELINE_METADATA: &str = "timeline-metadata"; -const ROOM_EVENT_ID_POSITION: &str = "room-event-id-to-position"; +const TIMELINE: &str = "timeline"; type Result = std::result::Result; From 07f01eb985e94a8a89284d636a94674073de9396 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 12:26:27 +0200 Subject: [PATCH 26/32] typos --- .../src/store/integration_tests.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index ba1fc542e..b583fff20 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -277,18 +277,18 @@ macro_rules! statestore_integration_tests { assert!(store.get_sync_token().await?.is_some()); assert!(store.get_presence_event(user_id).await?.is_some()); - assert_eq!(store.get_room_infos().await?.len(), 1, "Exepcted to find 1 room info "); - assert_eq!(store.get_stripped_room_infos().await?.len(), 1, "Exepcted to find 1 stripped room info"); + assert_eq!(store.get_room_infos().await?.len(), 1, "Expected to find 1 room info "); + assert_eq!(store.get_stripped_room_infos().await?.len(), 1, "Expected to find 1 stripped room info"); assert!(store.get_account_data_event(GlobalAccountDataEventType::PushRules).await?.is_some()); assert!(store.get_state_event(room_id, StateEventType::RoomName, "").await?.is_some()); - assert_eq!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(), 1, "Exepcted to find 1 room topic"); + assert_eq!(store.get_state_events(room_id, StateEventType::RoomTopic).await?.len(), 1, "Expected to find 1 room topic"); assert!(store.get_profile(room_id, user_id).await?.is_some()); assert!(store.get_member_event(room_id, user_id).await?.is_some()); - assert_eq!(store.get_user_ids(room_id).await?.len(), 2, "expected to find 2 members for room"); - assert_eq!(store.get_invited_user_ids(room_id).await?.len(), 1, "Exepcted to find 1 invited user ids"); - assert_eq!(store.get_joined_user_ids(room_id).await?.len(), 1, "Exepcted to find 1 joined user ids"); - assert_eq!(store.get_users_with_display_name(room_id, "example").await?.len(), 2, "expected to find 2 display names for room"); + assert_eq!(store.get_user_ids(room_id).await?.len(), 2, "Expected to find 2 members for room"); + assert_eq!(store.get_invited_user_ids(room_id).await?.len(), 1, "Expected to find 1 invited user ids"); + assert_eq!(store.get_joined_user_ids(room_id).await?.len(), 1, "Expected to find 1 joined user ids"); + assert_eq!(store.get_users_with_display_name(room_id, "example").await?.len(), 2, "Expected to find 2 display names for room"); assert!(store .get_room_account_data_event(room_id, RoomAccountDataEventType::Tag) .await? @@ -302,7 +302,7 @@ macro_rules! statestore_integration_tests { .get_event_room_receipt_events(room_id, ReceiptType::Read, first_receipt_event_id()) .await? .len(), - 1, "Exepcted to find 1 read receipt"); + 1, "Expected to find 1 read receipt"); Ok(()) } From ac8500759c48abb181982fcf8230af7cd8bedc70 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 12:27:11 +0200 Subject: [PATCH 27/32] newline --- crates/matrix-sdk-indexeddb/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml index 297c72b70..0ddf6117f 100644 --- a/crates/matrix-sdk-indexeddb/Cargo.toml +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -38,4 +38,4 @@ matrix-sdk-base = { path = "../matrix-sdk-base", features = ["testing"] } matrix-sdk-crypto = { path = "../matrix-sdk-crypto", features = ["testing"] } matrix-sdk-test = { path = "../matrix-sdk-test" } uuid = "0.8" -wasm-bindgen-test = "0.3.24" \ No newline at end of file +wasm-bindgen-test = "0.3.24" From 88d1aa615cf348267e866419eb64a4b1d85f819e Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 12:42:39 +0200 Subject: [PATCH 28/32] remove unnecessary reference making --- crates/matrix-sdk-sled/src/cryptostore.rs | 4 +- crates/matrix-sdk-sled/src/state_store.rs | 58 +++++++++++------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/crates/matrix-sdk-sled/src/cryptostore.rs b/crates/matrix-sdk-sled/src/cryptostore.rs index 2c30c638a..53ff721f4 100644 --- a/crates/matrix-sdk-sled/src/cryptostore.rs +++ b/crates/matrix-sdk-sled/src/cryptostore.rs @@ -287,10 +287,10 @@ impl SledStore { } } - fn encode_key( + fn encode_key( &self, table_name: &str, - key: &T, + key: T, ) -> Vec { if let Some(store_cipher) = &*self.store_cipher { key.encode_secure(table_name, store_cipher).to_vec() diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 42d7d8d3a..2a0c1028a 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -320,10 +320,10 @@ impl SledStore { } } - fn encode_key( + fn encode_key( &self, table_name: &str, - key: &T, + key: T, ) -> Vec { if let Some(store_cipher) = &*self.store_cipher { key.encode_secure(table_name, store_cipher).to_vec() @@ -332,10 +332,10 @@ impl SledStore { } } - fn encode_key_with_counter( + fn encode_key_with_counter( &self, tablename: &str, - s: &A, + s: A, i: usize, ) -> Vec { [ @@ -348,14 +348,14 @@ impl SledStore { } pub async fn save_filter(&self, filter_name: &str, filter_id: &str) -> Result<()> { - self.session.insert(self.encode_key(SESSION, &("filter", filter_name)), filter_id)?; + self.session.insert(self.encode_key(SESSION, ("filter", filter_name)), filter_id)?; Ok(()) } pub async fn get_filter(&self, filter_name: &str) -> Result> { Ok(self .session - .get(self.encode_key(SESSION, &("filter", filter_name)))? + .get(self.encode_key(SESSION, ("filter", filter_name)))? .map(|f| String::from_utf8_lossy(&f).to_string())) } @@ -454,7 +454,7 @@ impl SledStore { for (room_id, ambiguity_maps) in &changes.ambiguity_maps { for (display_name, map) in ambiguity_maps { display_names.insert( - self.encode_key(DISPLAY_NAME, &(room_id, display_name)), + self.encode_key(DISPLAY_NAME, (room_id, display_name)), self.serialize_event(&map) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -483,7 +483,7 @@ impl SledStore { for (event_type, events) in event_types { for (state_key, event) in events { state.insert( - self.encode_key(ROOM_STATE, &(room, event_type, state_key)), + self.encode_key(ROOM_STATE, (room, event_type, state_key)), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, )?; @@ -520,7 +520,7 @@ impl SledStore { stripped_members.insert( self.encode_key( STRIPPED_ROOM_MEMBER, - &(room, event.state_key.to_string()), + (room, event.state_key.to_string()), ), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, @@ -534,7 +534,7 @@ impl SledStore { stripped_state.insert( self.encode_key( STRIPPED_ROOM_STATE, - &(room, event_type.to_string(), state_key), + (room, event_type.to_string(), state_key), ), self.serialize_event(&event) .map_err(ConflictableTransactionError::Abort)?, @@ -560,7 +560,7 @@ impl SledStore { if let Some(old) = room_user_receipts.insert( self.encode_key( ROOM_USER_RECEIPT, - &(room, receipt_type, user_id), + (room, receipt_type, user_id), ), self.serialize_event(&(event_id, receipt)) .map_err(ConflictableTransactionError::Abort)?, @@ -571,7 +571,7 @@ impl SledStore { .map_err(ConflictableTransactionError::Abort)?; room_event_receipts.remove(self.encode_key( ROOM_EVENT_RECEIPT, - &(room, receipt_type, old_event, user_id), + (room, receipt_type, old_event, user_id), ))?; } @@ -579,7 +579,7 @@ impl SledStore { room_event_receipts.insert( self.encode_key( ROOM_EVENT_RECEIPT, - &(room, receipt_type, event_id, user_id), + (room, receipt_type, event_id, user_id), ), self.serialize_event(&(user_id, receipt)) .map_err(ConflictableTransactionError::Abort)?, @@ -618,7 +618,7 @@ impl SledStore { state_key: &str, ) -> Result>> { let db = self.clone(); - let key = self.encode_key(ROOM_STATE, &(room_id, event_type.to_string(), state_key)); + let key = self.encode_key(ROOM_STATE, (room_id, event_type.to_string(), state_key)); spawn_blocking(move || { db.room_state.get(key)?.map(|e| db.deserialize_event(&e)).transpose() }) @@ -631,7 +631,7 @@ impl SledStore { event_type: StateEventType, ) -> Result>> { let db = self.clone(); - let key = self.encode_key(ROOM_STATE, &(room_id, event_type.to_string())); + let key = self.encode_key(ROOM_STATE, (room_id, event_type.to_string())); spawn_blocking(move || { db.room_state .scan_prefix(key) @@ -647,7 +647,7 @@ impl SledStore { user_id: &UserId, ) -> Result> { let db = self.clone(); - let key = self.encode_key(PROFILE, &(room_id, user_id)); + let key = self.encode_key(PROFILE, (room_id, user_id)); spawn_blocking(move || db.profiles.get(key)?.map(|p| db.deserialize_event(&p)).transpose()) .await? } @@ -658,7 +658,7 @@ impl SledStore { state_key: &UserId, ) -> Result> { let db = self.clone(); - let key = self.encode_key(MEMBER, &(room_id, state_key)); + let key = self.encode_key(MEMBER, (room_id, state_key)); spawn_blocking(move || db.members.get(key)?.map(|v| db.deserialize_event(&v)).transpose()) .await? } @@ -735,7 +735,7 @@ impl SledStore { display_name: &str, ) -> Result>> { let db = self.clone(); - let key = self.encode_key(DISPLAY_NAME, &(room_id, display_name)); + let key = self.encode_key(DISPLAY_NAME, (room_id, display_name)); spawn_blocking(move || { Ok(db .display_names @@ -752,7 +752,7 @@ impl SledStore { event_type: GlobalAccountDataEventType, ) -> Result>> { let db = self.clone(); - let key = self.encode_key(ACCOUNT_DATA, &event_type); + let key = self.encode_key(ACCOUNT_DATA, event_type); spawn_blocking(move || { db.account_data.get(key)?.map(|m| db.deserialize_event(&m)).transpose() }) @@ -765,7 +765,7 @@ impl SledStore { event_type: RoomAccountDataEventType, ) -> Result>> { let db = self.clone(); - let key = self.encode_key(ROOM_ACCOUNT_DATA, &(room_id, event_type)); + let key = self.encode_key(ROOM_ACCOUNT_DATA, (room_id, event_type)); spawn_blocking(move || { db.room_account_data.get(key)?.map(|m| db.deserialize_event(&m)).transpose() }) @@ -779,7 +779,7 @@ impl SledStore { user_id: &UserId, ) -> Result, Receipt)>> { let db = self.clone(); - let key = self.encode_key(ROOM_USER_RECEIPT, &(room_id, receipt_type, user_id)); + let key = self.encode_key(ROOM_USER_RECEIPT, (room_id, receipt_type, user_id)); spawn_blocking(move || { db.room_user_receipts.get(key)?.map(|m| db.deserialize_event(&m)).transpose() }) @@ -793,7 +793,7 @@ impl SledStore { event_id: &EventId, ) -> StoreResult, Receipt)>> { let db = self.clone(); - let key = self.encode_key(ROOM_EVENT_RECEIPT, &(room_id, receipt_type, event_id)); + let key = self.encode_key(ROOM_EVENT_RECEIPT, (room_id, receipt_type, event_id)); spawn_blocking(move || { db.room_event_receipts .scan_prefix(key) @@ -810,7 +810,7 @@ impl SledStore { async fn add_media_content(&self, request: &MediaRequest, data: Vec) -> Result<()> { self.media.insert( - self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())), + self.encode_key(MEDIA, (request.source.unique_key(), request.format.unique_key())), data, )?; @@ -822,7 +822,7 @@ impl SledStore { async fn get_media_content(&self, request: &MediaRequest) -> Result>> { let db = self.clone(); let key = - self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())); + self.encode_key(MEDIA, (request.source.unique_key(), request.format.unique_key())); spawn_blocking(move || Ok(db.media.get(key)?.map(|m| m.to_vec()))).await? } @@ -842,7 +842,7 @@ impl SledStore { async fn remove_media_content(&self, request: &MediaRequest) -> Result<()> { self.media.remove( - self.encode_key(MEDIA, &(request.source.unique_key(), request.format.unique_key())), + self.encode_key(MEDIA, (request.source.unique_key(), request.format.unique_key())), )?; Ok(()) @@ -1101,7 +1101,7 @@ impl SledStore { for event in &timeline.events { if let Some(event_id) = event.event_id() { let event_key = - self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); + self.encode_key(ROOM_EVENT_ID_POSITION, (room_id, event_id)); if self.room_event_id_to_position.contains_key(event_key)? { delete_timeline = true; break; @@ -1159,7 +1159,7 @@ impl SledStore { )) = event.event.deserialize() { let redacts_key = - self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, redaction.redacts)); + self.encode_key(ROOM_EVENT_ID_POSITION, (room_id, redaction.redacts)); if let Some(position_key) = self.room_event_id_to_position.get(redacts_key)? { @@ -1190,7 +1190,7 @@ impl SledStore { // Only add event with id to the position map if let Some(event_id) = event.event_id() { let event_key = - self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); + self.encode_key(ROOM_EVENT_ID_POSITION, (room_id, event_id)); event_id_to_position_batch.insert(event_key.as_slice(), key.as_slice()); } } @@ -1203,7 +1203,7 @@ impl SledStore { // Only add event with id to the position map if let Some(event_id) = event.event_id() { let event_key = - self.encode_key(ROOM_EVENT_ID_POSITION, &(room_id, event_id)); + self.encode_key(ROOM_EVENT_ID_POSITION, (room_id, event_id)); event_id_to_position_batch.insert(event_key.as_slice(), key.as_slice()); } } From e989b9aff7162b83f32c45b32a7e5deaac097c58 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 16:01:18 +0200 Subject: [PATCH 29/32] merge sledstore's EncodeKey and SecureEncodeKey into one --- .../matrix-sdk-indexeddb/src/safe_encode.rs | 3 +- crates/matrix-sdk-sled/src/cryptostore.rs | 95 ++---- crates/matrix-sdk-sled/src/encode_key.rs | 297 +++++++----------- crates/matrix-sdk-sled/src/state_store.rs | 15 +- 4 files changed, 149 insertions(+), 261 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/src/safe_encode.rs b/crates/matrix-sdk-indexeddb/src/safe_encode.rs index 725c27300..2d20b5c9e 100644 --- a/crates/matrix-sdk-indexeddb/src/safe_encode.rs +++ b/crates/matrix-sdk-indexeddb/src/safe_encode.rs @@ -76,7 +76,8 @@ pub trait SafeEncode { self.as_secure_string(table_name, store_cipher), KEY_SEPARATOR, i - ).into() + ) + .into() } /// Encode self into a IdbKeyRange for searching all keys that are diff --git a/crates/matrix-sdk-sled/src/cryptostore.rs b/crates/matrix-sdk-sled/src/cryptostore.rs index 53ff721f4..eaa9b781b 100644 --- a/crates/matrix-sdk-sled/src/cryptostore.rs +++ b/crates/matrix-sdk-sled/src/cryptostore.rs @@ -24,10 +24,7 @@ use dashmap::DashSet; use matrix_sdk_common::{ async_trait, locks::Mutex, - ruma::{ - events::{room_key_request::RequestedKeyInfo, secret::request::SecretName}, - DeviceId, RoomId, TransactionId, UserId, - }, + ruma::{events::room_key_request::RequestedKeyInfo, DeviceId, RoomId, TransactionId, UserId}, }; use matrix_sdk_crypto::{ olm::{ @@ -50,7 +47,7 @@ use sled::{ use tracing::debug; use super::OpenStoreError; -use crate::encode_key::{EncodeKey, EncodeSecureKey, ENCODE_SEPARATOR}; +use crate::encode_key::{EncodeKey, ENCODE_SEPARATOR}; const DATABASE_VERSION: u8 = 4; @@ -69,12 +66,20 @@ impl EncodeKey for InboundGroupSession { fn encode(&self) -> Vec { (self.room_id(), self.sender_key(), self.session_id()).encode() } + + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + (self.room_id(), self.sender_key(), self.session_id()) + .encode_secure(table_name, store_cipher) + } } impl EncodeKey for OutboundGroupSession { fn encode(&self) -> Vec { self.room_id().encode() } + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + self.room_id().encode_secure(table_name, store_cipher) + } } impl EncodeKey for Session { @@ -85,6 +90,15 @@ impl EncodeKey for Session { [sender_key.as_bytes(), &[ENCODE_SEPARATOR], session_id.as_bytes(), &[ENCODE_SEPARATOR]] .concat() } + + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let sender_key = + store_cipher.hash_key(table_name, self.sender_key().to_base64().as_bytes()); + let session_id = store_cipher.hash_key(table_name, self.session_id().as_bytes()); + + [sender_key.as_slice(), &[ENCODE_SEPARATOR], session_id.as_slice(), &[ENCODE_SEPARATOR]] + .concat() + } } impl EncodeKey for SecretInfo { @@ -94,31 +108,6 @@ impl EncodeKey for SecretInfo { SecretInfo::SecretRequest(s) => s.encode(), } } -} - -impl EncodeKey for RequestedKeyInfo { - fn encode(&self) -> Vec { - (&self.room_id, &self.sender_key, &self.algorithm, &self.session_id).encode() - } -} - -impl EncodeKey for ReadOnlyDevice { - fn encode(&self) -> Vec { - (self.user_id(), self.device_id()).encode() - } -} - -impl EncodeSecureKey for (&UserId, &DeviceId) { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let user_id = store_cipher.hash_key(table_name, self.0.as_bytes()); - let device_id = store_cipher.hash_key(table_name, self.1.as_bytes()); - - [user_id.as_slice(), &[ENCODE_SEPARATOR], device_id.as_slice(), &[ENCODE_SEPARATOR]] - .concat() - } -} - -impl EncodeSecureKey for SecretInfo { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { match self { SecretInfo::KeyRequest(k) => k.encode_secure(table_name, store_cipher), @@ -127,15 +116,10 @@ impl EncodeSecureKey for SecretInfo { } } -impl EncodeSecureKey for SecretName { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let name = store_cipher.hash_key(table_name, self.as_ref().as_bytes()); - - [name.as_slice(), &[ENCODE_SEPARATOR]].concat() +impl EncodeKey for RequestedKeyInfo { + fn encode(&self) -> Vec { + (&self.room_id, &self.sender_key, &self.algorithm, &self.session_id).encode() } -} - -impl EncodeSecureKey for RequestedKeyInfo { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { let room_id = store_cipher.hash_key(table_name, self.room_id.as_bytes()); let sender_key = store_cipher.hash_key(table_name, self.sender_key.as_bytes()); @@ -156,36 +140,15 @@ impl EncodeSecureKey for RequestedKeyInfo { } } -impl EncodeSecureKey for ReadOnlyDevice { +impl EncodeKey for ReadOnlyDevice { + fn encode(&self) -> Vec { + (self.user_id(), self.device_id()).encode() + } fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { (self.user_id(), self.device_id()).encode_secure(table_name, store_cipher) } } -impl EncodeSecureKey for OutboundGroupSession { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - self.room_id().encode_secure(table_name, store_cipher) - } -} - -impl EncodeSecureKey for InboundGroupSession { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - (self.room_id(), self.sender_key(), self.session_id()) - .encode_secure(table_name, store_cipher) - } -} - -impl EncodeSecureKey for Session { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let sender_key = - store_cipher.hash_key(table_name, self.sender_key().to_base64().as_bytes()); - let session_id = store_cipher.hash_key(table_name, self.session_id().as_bytes()); - - [sender_key.as_slice(), &[ENCODE_SEPARATOR], session_id.as_slice(), &[ENCODE_SEPARATOR]] - .concat() - } -} - #[derive(Clone, Debug)] pub struct AccountInfo { user_id: Arc, @@ -287,11 +250,7 @@ impl SledStore { } } - fn encode_key( - &self, - table_name: &str, - key: T, - ) -> Vec { + fn encode_key(&self, table_name: &str, key: T) -> Vec { if let Some(store_cipher) = &*self.store_cipher { key.encode_secure(table_name, store_cipher).to_vec() } else { diff --git a/crates/matrix-sdk-sled/src/encode_key.rs b/crates/matrix-sdk-sled/src/encode_key.rs index 8ae46ea40..2440966a2 100644 --- a/crates/matrix-sdk-sled/src/encode_key.rs +++ b/crates/matrix-sdk-sled/src/encode_key.rs @@ -1,3 +1,5 @@ +use std::{borrow::Cow, ops::Deref}; + use matrix_sdk_common::ruma::{ events::{ secret::request::SecretName, GlobalAccountDataEventType, RoomAccountDataEventType, @@ -11,94 +13,128 @@ use matrix_sdk_store_encryption::StoreCipher; pub const ENCODE_SEPARATOR: u8 = 0xff; pub trait EncodeKey { - fn encode(&self) -> Vec; + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + unimplemented!() + } + + fn encode(&self) -> Vec { + [self.encode_as_bytes().deref(), &[ENCODE_SEPARATOR]].concat() + } + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let key = store_cipher.hash_key(table_name, &self.encode_as_bytes()); + [key.as_slice(), &[ENCODE_SEPARATOR]].concat() + } } impl EncodeKey for &T { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + T::encode_as_bytes(self) + } fn encode(&self) -> Vec { T::encode(self) } + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + T::encode_secure(self, table_name, store_cipher) + } } impl EncodeKey for Box { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + T::encode_as_bytes(self) + } fn encode(&self) -> Vec { T::encode(self) } + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + T::encode_secure(self, table_name, store_cipher) + } } impl EncodeKey for str { - fn encode(&self) -> Vec { - [self.as_bytes(), &[ENCODE_SEPARATOR]].concat() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_bytes().into() } } impl EncodeKey for String { - fn encode(&self) -> Vec { - self.as_str().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_str().as_bytes().into() } } impl EncodeKey for DeviceId { - fn encode(&self) -> Vec { - self.as_str().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_str().as_bytes().into() } } impl EncodeKey for EventId { - fn encode(&self) -> Vec { - self.as_str().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_str().as_bytes().into() } } impl EncodeKey for RoomId { - fn encode(&self) -> Vec { - self.as_str().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_str().as_bytes().into() } } impl EncodeKey for TransactionId { - fn encode(&self) -> Vec { - self.as_str().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_str().as_bytes().into() } } impl EncodeKey for MxcUri { - fn encode(&self) -> Vec { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { let s: &str = self.as_ref(); - s.encode() + s.as_bytes().into() } } impl EncodeKey for SecretName { - fn encode(&self) -> Vec { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { let s: &str = self.as_ref(); - s.encode() + s.as_bytes().into() } } impl EncodeKey for ReceiptType { - fn encode(&self) -> Vec { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { let s: &str = self.as_ref(); - s.encode() + s.as_bytes().into() } } impl EncodeKey for EventEncryptionAlgorithm { - fn encode(&self) -> Vec { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { let s: &str = self.as_ref(); - s.encode() + s.as_bytes().into() } } impl EncodeKey for RoomAccountDataEventType { - fn encode(&self) -> Vec { - self.to_string().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.to_string().as_bytes().to_vec().into() } } impl EncodeKey for UserId { - fn encode(&self) -> Vec { - self.as_str().encode() + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.as_str().as_bytes().into() + } +} + +impl EncodeKey for StateEventType { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.to_string().as_bytes().to_vec().into() + } +} + +impl EncodeKey for GlobalAccountDataEventType { + fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + self.to_string().as_bytes().to_vec().into() } } @@ -108,7 +144,23 @@ where B: EncodeKey, { fn encode(&self) -> Vec { - [self.0.encode(), self.1.encode()].concat() + [ + self.0.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + self.1.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + ] + .concat() + } + + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + ] + .concat() } } @@ -119,7 +171,27 @@ where C: EncodeKey, { fn encode(&self) -> Vec { - [self.0.encode(), self.1.encode(), self.2.encode()].concat() + [ + self.0.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + self.1.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + self.2.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + ] + .concat() + } + + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + [ + store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + ] + .concat() } } @@ -131,165 +203,30 @@ where D: EncodeKey, { fn encode(&self) -> Vec { - [self.0.encode(), self.1.encode(), self.2.encode(), self.3.encode()].concat() + [ + self.0.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + self.1.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + self.2.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + self.3.encode_as_bytes().deref(), + &[ENCODE_SEPARATOR], + ] + .concat() } -} -impl EncodeKey for StateEventType { - fn encode(&self) -> Vec { - self.to_string().encode() - } -} - -impl EncodeKey for GlobalAccountDataEventType { - fn encode(&self) -> Vec { - self.to_string().encode() - } -} - -pub trait EncodeSecureKey { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec; -} - -impl EncodeSecureKey for &T { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - T::encode_secure(self, table_name, store_cipher) - } -} - -impl EncodeSecureKey for Box { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - T::encode_secure(self, table_name, store_cipher) - } -} - -impl EncodeSecureKey for UserId { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let user_id = store_cipher.hash_key(table_name, self.as_bytes()); - - [user_id.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for str { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let key = store_cipher.hash_key(table_name, self.as_bytes()); - [key.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for String { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let key = store_cipher.hash_key(table_name, self.as_bytes()); - [key.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for RoomId { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let room_id = store_cipher.hash_key(table_name, self.as_bytes()); - - [room_id.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for EventId { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let event_id = store_cipher.hash_key(table_name, self.as_bytes()); - - [event_id.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for MxcUri { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let s: &str = self.as_ref(); - [store_cipher.hash_key(table_name, s.as_bytes()).as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for ReceiptType { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - let event_id = store_cipher.hash_key(table_name, self.as_str().as_bytes()); - - [event_id.as_slice(), &[ENCODE_SEPARATOR]].concat() - } -} - -impl EncodeSecureKey for GlobalAccountDataEventType { fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { [ - store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), + store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(), + &[ENCODE_SEPARATOR], + store_cipher.hash_key(table_name, &self.3.encode_as_bytes()).as_slice(), &[ENCODE_SEPARATOR], ] .concat() } } - -impl EncodeSecureKey for StateEventType { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - [ - store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), - &[ENCODE_SEPARATOR], - ] - .concat() - } -} - -impl EncodeSecureKey for RoomAccountDataEventType { - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - [ - store_cipher.hash_key(table_name, self.to_string().as_bytes()).as_slice(), - &[ENCODE_SEPARATOR], - ] - .concat() - } -} - -impl EncodeSecureKey for (A, B) -where - A: EncodeSecureKey, - B: EncodeSecureKey, -{ - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - [ - self.0.encode_secure(table_name, store_cipher), - self.1.encode_secure(table_name, store_cipher), - ] - .concat() - } -} - -impl EncodeSecureKey for (A, B, C) -where - A: EncodeSecureKey, - B: EncodeSecureKey, - C: EncodeSecureKey, -{ - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - [ - self.0.encode_secure(table_name, store_cipher), - self.1.encode_secure(table_name, store_cipher), - self.2.encode_secure(table_name, store_cipher), - ] - .concat() - } -} - -impl EncodeSecureKey for (A, B, C, D) -where - A: EncodeSecureKey, - B: EncodeSecureKey, - C: EncodeSecureKey, - D: EncodeSecureKey, -{ - fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { - [ - self.0.encode_secure(table_name, store_cipher), - self.1.encode_secure(table_name, store_cipher), - self.2.encode_secure(table_name, store_cipher), - self.3.encode_secure(table_name, store_cipher), - ] - .concat() - } -} diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 2a0c1028a..2fbe51c00 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -59,7 +59,7 @@ use tracing::{info, warn}; #[cfg(feature = "crypto-store")] use super::OpenStoreError; -use crate::encode_key::{EncodeKey, EncodeSecureKey, ENCODE_SEPARATOR}; +use crate::encode_key::{EncodeKey, ENCODE_SEPARATOR}; #[cfg(feature = "crypto-store")] pub use crate::CryptoStore; @@ -320,11 +320,7 @@ impl SledStore { } } - fn encode_key( - &self, - table_name: &str, - key: T, - ) -> Vec { + fn encode_key(&self, table_name: &str, key: T) -> Vec { if let Some(store_cipher) = &*self.store_cipher { key.encode_secure(table_name, store_cipher).to_vec() } else { @@ -332,12 +328,7 @@ impl SledStore { } } - fn encode_key_with_counter( - &self, - tablename: &str, - s: A, - i: usize, - ) -> Vec { + fn encode_key_with_counter(&self, tablename: &str, s: A, i: usize) -> Vec { [ &self.encode_key(tablename, s), [ENCODE_SEPARATOR].as_slice(), From cdab735ce54096562974565736a59d27e110d302 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 16:19:30 +0200 Subject: [PATCH 30/32] Apply suggestions from code review Co-authored-by: Jonas Platte --- crates/matrix-sdk-base/src/store/integration_tests.rs | 4 ++-- crates/matrix-sdk-store-encryption/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index b583fff20..eacd563ed 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -277,8 +277,8 @@ macro_rules! statestore_integration_tests { assert!(store.get_sync_token().await?.is_some()); assert!(store.get_presence_event(user_id).await?.is_some()); - assert_eq!(store.get_room_infos().await?.len(), 1, "Expected to find 1 room info "); - assert_eq!(store.get_stripped_room_infos().await?.len(), 1, "Expected to find 1 stripped room info"); + assert_eq!(store.get_room_infos().await?.len(), 1, "Expected to find 1 room info "); + assert_eq!(store.get_stripped_room_infos().await?.len(), 1, "Expected to find 1 stripped room info"); assert!(store.get_account_data_event(GlobalAccountDataEventType::PushRules).await?.is_some()); assert!(store.get_state_event(room_id, StateEventType::RoomName, "").await?.is_some()); diff --git a/crates/matrix-sdk-store-encryption/src/lib.rs b/crates/matrix-sdk-store-encryption/src/lib.rs index 68612b1c5..67a8905fc 100644 --- a/crates/matrix-sdk-store-encryption/src/lib.rs +++ b/crates/matrix-sdk-store-encryption/src/lib.rs @@ -303,7 +303,7 @@ impl StoreCipher { /// /// # Examples /// - /// ``` + /// ```no_run /// # let example = || { /// use matrix_sdk_store_encryption::StoreCipher; /// use serde_json::{json, value::Value}; @@ -318,7 +318,7 @@ impl StoreCipher { /// let decrypted: Value = store_cipher.decrypt_value_typed(&encrypted)?; /// /// assert_eq!(value, decrypted); - /// # Result::<_, anyhow::Error>::Ok(()) }; + /// # anyhow::Ok(()) }; /// ``` pub fn encrypt_value_typed(&self, value: &impl Serialize) -> Result { let data = serde_json::to_vec(value)?; From 4da7d317705b4b751b5ebf07767df15e104168b0 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 16:22:25 +0200 Subject: [PATCH 31/32] fixing unnecessary lifetimes --- crates/matrix-sdk-sled/src/encode_key.rs | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/matrix-sdk-sled/src/encode_key.rs b/crates/matrix-sdk-sled/src/encode_key.rs index 2440966a2..7bdcfdb21 100644 --- a/crates/matrix-sdk-sled/src/encode_key.rs +++ b/crates/matrix-sdk-sled/src/encode_key.rs @@ -13,7 +13,7 @@ use matrix_sdk_store_encryption::StoreCipher; pub const ENCODE_SEPARATOR: u8 = 0xff; pub trait EncodeKey { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { unimplemented!() } @@ -27,7 +27,7 @@ pub trait EncodeKey { } impl EncodeKey for &T { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { T::encode_as_bytes(self) } fn encode(&self) -> Vec { @@ -39,7 +39,7 @@ impl EncodeKey for &T { } impl EncodeKey for Box { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { T::encode_as_bytes(self) } fn encode(&self) -> Vec { @@ -51,89 +51,89 @@ impl EncodeKey for Box { } impl EncodeKey for str { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_bytes().into() } } impl EncodeKey for String { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_str().as_bytes().into() } } impl EncodeKey for DeviceId { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_str().as_bytes().into() } } impl EncodeKey for EventId { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_str().as_bytes().into() } } impl EncodeKey for RoomId { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_str().as_bytes().into() } } impl EncodeKey for TransactionId { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_str().as_bytes().into() } } impl EncodeKey for MxcUri { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { let s: &str = self.as_ref(); s.as_bytes().into() } } impl EncodeKey for SecretName { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { let s: &str = self.as_ref(); s.as_bytes().into() } } impl EncodeKey for ReceiptType { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { let s: &str = self.as_ref(); s.as_bytes().into() } } impl EncodeKey for EventEncryptionAlgorithm { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { let s: &str = self.as_ref(); s.as_bytes().into() } } impl EncodeKey for RoomAccountDataEventType { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.to_string().as_bytes().to_vec().into() } } impl EncodeKey for UserId { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.as_str().as_bytes().into() } } impl EncodeKey for StateEventType { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.to_string().as_bytes().to_vec().into() } } impl EncodeKey for GlobalAccountDataEventType { - fn encode_as_bytes<'a>(&'a self) -> Cow<'a, [u8]> { + fn encode_as_bytes(&self) -> Cow<'_, [u8]> { self.to_string().as_bytes().to_vec().into() } } From 07c5f625d4d79329e8e3d3409ac404f1181df2cd Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Tue, 19 Apr 2022 16:30:20 +0200 Subject: [PATCH 32/32] fixing encryption examples --- crates/matrix-sdk-store-encryption/src/lib.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/matrix-sdk-store-encryption/src/lib.rs b/crates/matrix-sdk-store-encryption/src/lib.rs index 67a8905fc..0e0ad8d17 100644 --- a/crates/matrix-sdk-store-encryption/src/lib.rs +++ b/crates/matrix-sdk-store-encryption/src/lib.rs @@ -315,7 +315,7 @@ impl StoreCipher { /// }); /// /// let encrypted = store_cipher.encrypt_value_typed(&value)?; - /// let decrypted: Value = store_cipher.decrypt_value_typed(&encrypted)?; + /// let decrypted: Value = store_cipher.decrypt_value_typed(encrypted)?; /// /// assert_eq!(value, decrypted); /// # anyhow::Ok(()) }; @@ -343,12 +343,12 @@ impl StoreCipher { /// /// let store_cipher = StoreCipher::new()?; /// - /// let value = serde_json::to_vec(json!({ + /// let value = serde_json::to_vec(&json!({ /// "some": "data", - /// })); + /// }))?; /// - /// let encrypted = store_cipher.encrypt_value_data(&value)?; - /// let decrypted: Value = store_cipher.decrypt_value_data(&encrypted)?; + /// let encrypted = store_cipher.encrypt_value_data(value.clone())?; + /// let decrypted = store_cipher.decrypt_value_data(encrypted)?; /// /// assert_eq!(value, decrypted); /// # Result::<_, anyhow::Error>::Ok(()) }; @@ -424,7 +424,7 @@ impl StoreCipher { /// }); /// /// let encrypted = store_cipher.encrypt_value_typed(&value)?; - /// let decrypted: Value = store_cipher.decrypt_value_typed(&encrypted)?; + /// let decrypted: Value = store_cipher.decrypt_value_typed(encrypted)?; /// /// assert_eq!(value, decrypted); /// # Result::<_, anyhow::Error>::Ok(()) }; @@ -459,12 +459,12 @@ impl StoreCipher { /// /// let store_cipher = StoreCipher::new()?; /// - /// let value = serde_json::to_vec(json!({ + /// let value = serde_json::to_vec(&json!({ /// "some": "data", - /// })); + /// }))?; /// - /// let encrypted = store_cipher.encrypt_value_data(&value)?; - /// let decrypted = store_cipher.decrypt_value_data(&encrypted)?; + /// let encrypted = store_cipher.encrypt_value_data(value.clone())?; + /// let decrypted = store_cipher.decrypt_value_data(encrypted)?; /// /// assert_eq!(value, decrypted); /// # Result::<_, anyhow::Error>::Ok(()) };