fixing style and typos

This commit is contained in:
Benjamin Kampmann
2022-04-19 09:23:29 +02:00
parent 9698a50ad8
commit 80791860fe
7 changed files with 286 additions and 173 deletions

View File

@@ -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");

View File

@@ -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<IdbKeyRange, String> {
fn encode_to_range_secure(
&self,
table_name: &str,
store_cipher: &StoreCipher,
) -> Result<IdbKeyRange, String> {
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()
}
}

View File

@@ -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<SerializationError> 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<T>(&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<T>(&self, table_name: &str, key: T) -> Result<IdbKeyRange, SerializationError>
where T: SafeEncode
fn encode_to_range<T>(
&self,
table_name: &str,
key: T,
) -> Result<IdbKeyRange, SerializationError>
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<T>(&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<EventId>, 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<Vec<Box<UserId>>> {
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<Vec<Box<UserId>>> {
@@ -934,7 +978,8 @@ impl IndexeddbStore {
receipt_type: ReceiptType,
event_id: &EventId,
) -> Result<Vec<(Box<UserId>, 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<u8>) -> 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<Option<Vec<u8>>> {
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<IndexeddbStore> {
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 }
}

View File

@@ -186,7 +186,6 @@ impl EncodeSecureKey for Session {
}
}
#[derive(Clone, Debug)]
pub struct AccountInfo {
user_id: Arc<UserId>,

View File

@@ -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<u8> {
[
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<u8> {
[
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<u8> {
[
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<u8>;
}
impl<T: EncodeSecureKey + ?Sized> EncodeSecureKey for &T {
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
T::encode_secure(self, table_name, store_cipher)
@@ -179,7 +163,6 @@ impl<T: EncodeSecureKey + ?Sized> EncodeSecureKey for Box<T> {
}
}
impl EncodeSecureKey for UserId {
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
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<u8> {
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<A, B> EncodeSecureKey for (A, B)
where
A: EncodeSecureKey,
@@ -310,4 +292,4 @@ where
]
.concat()
}
}
}

View File

@@ -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<StoreError> 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<StoreError> 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<PathBuf>, store_cipher: Option<StoreCipher>) -> Result<Self> {
fn open_helper(
db: Db,
path: Option<PathBuf>,
store_cipher: Option<StoreCipher>,
) -> Result<Self> {
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<Path>, passphrase: &str) -> StoreResult<Self> {
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<T: EncodeSecureKey + EncodeKey + ?Sized>(
&self,
table_name: &str,
@@ -324,14 +335,15 @@ impl SledStore {
&self,
tablename: &str,
s: &A,
i: usize
i: usize,
) -> Vec<u8> {
[
&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<EventId>, 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<impl Stream<Item = StoreResult<Box<UserId>>>> {
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<u8>) -> 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<Option<Vec<u8>>> {
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<SledStoreError>> =

View File

@@ -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<T: for<'b> Deserialize<'b>>(&self, value: EncryptedValue) -> Result<T, Error> {
pub fn decrypt_value_typed<T: for<'b> Deserialize<'b>>(
&self,
value: EncryptedValue,
) -> Result<T, Error> {
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,