From bfe79468c6b5dd8cc0539e7f5375d7c76f69afeb Mon Sep 17 00:00:00 2001
From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
Date: Mon, 27 Nov 2023 15:59:49 +0000
Subject: [PATCH] Indexeddb: Groundwork for fixing `inbound_group_session`
lookups (#2884)
A set of non-functional changes which lay some groundwork in preparation for fixing vector-im/element-web#26488.
---
.../src/crypto_store/indexeddb_serializer.rs | 164 +++++++
.../src/crypto_store/migrations.rs | 138 ++++++
.../{crypto_store.rs => crypto_store/mod.rs} | 424 +++++-------------
crates/matrix-sdk-indexeddb/src/lib.rs | 2 +
.../src/serialize_bool_for_indexeddb.rs | 41 ++
5 files changed, 447 insertions(+), 322 deletions(-)
create mode 100644 crates/matrix-sdk-indexeddb/src/crypto_store/indexeddb_serializer.rs
create mode 100644 crates/matrix-sdk-indexeddb/src/crypto_store/migrations.rs
rename crates/matrix-sdk-indexeddb/src/{crypto_store.rs => crypto_store/mod.rs} (70%)
create mode 100644 crates/matrix-sdk-indexeddb/src/serialize_bool_for_indexeddb.rs
diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/indexeddb_serializer.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/indexeddb_serializer.rs
new file mode 100644
index 00000000000..9d21d622921
--- /dev/null
+++ b/crates/matrix-sdk-indexeddb/src/crypto_store/indexeddb_serializer.rs
@@ -0,0 +1,164 @@
+// Copyright 2023 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::sync::Arc;
+
+use gloo_utils::format::JsValueSerdeExt;
+use matrix_sdk_crypto::CryptoStoreError;
+use matrix_sdk_store_encryption::StoreCipher;
+use serde::{de::DeserializeOwned, Serialize};
+use wasm_bindgen::JsValue;
+use web_sys::IdbKeyRange;
+
+use crate::{safe_encode::SafeEncode, IndexeddbCryptoStoreError};
+
+type Result = std::result::Result;
+
+/// Handles the functionality of serializing and encrypting data for the
+/// indexeddb store.
+pub struct IndexeddbSerializer {
+ store_cipher: Option>,
+}
+
+impl IndexeddbSerializer {
+ pub fn new(store_cipher: Option>) -> Self {
+ Self { store_cipher }
+ }
+
+ /// Hash the given key securely for the given tablename, using the store
+ /// cipher.
+ ///
+ /// First calls [`SafeEncode::as_encoded_string`]
+ /// on the `key` to encode it into a formatted string.
+ ///
+ /// Then, if a cipher is configured, hashes the formatted key and returns
+ /// the hash encoded as unpadded base64.
+ ///
+ /// If no cipher is configured, just returns the formatted key.
+ ///
+ /// This is faster than [`serialize_value`] and reliably gives the same
+ /// output for the same input, making it suitable for index keys.
+ pub fn encode_key(&self, table_name: &str, key: T) -> JsValue
+ where
+ T: SafeEncode,
+ {
+ self.encode_key_as_string(table_name, key).into()
+ }
+
+ /// Hash the given key securely for the given tablename, using the store
+ /// cipher.
+ ///
+ /// The same as [`encode_key`], but stops short of converting the resulting
+ /// base64 string into a JsValue
+ pub fn encode_key_as_string(&self, table_name: &str, key: T) -> String
+ where
+ T: SafeEncode,
+ {
+ match &self.store_cipher {
+ Some(cipher) => key.as_secure_string(table_name, cipher),
+ None => key.as_encoded_string(),
+ }
+ }
+
+ pub fn encode_to_range(
+ &self,
+ table_name: &str,
+ key: T,
+ ) -> Result
+ where
+ T: SafeEncode,
+ {
+ match &self.store_cipher {
+ Some(cipher) => key.encode_to_range_secure(table_name, cipher),
+ None => key.encode_to_range(),
+ }
+ .map_err(|e| IndexeddbCryptoStoreError::DomException {
+ code: 0,
+ name: "IdbKeyRangeMakeError".to_owned(),
+ message: e,
+ })
+ }
+
+ /// Encode the value for storage as a value in indexeddb.
+ ///
+ /// First, serialise the given value as JSON.
+ ///
+ /// Then, if a store cipher is enabled, encrypt the JSON string using the
+ /// configured store cipher, giving a byte array. Then, wrap the byte
+ /// array as a `JsValue`.
+ ///
+ /// If no cipher is enabled, deserialises the JSON string again giving a JS
+ /// object.
+ pub fn serialize_value(&self, value: &impl Serialize) -> Result {
+ if let Some(cipher) = &self.store_cipher {
+ let value = cipher.encrypt_value(value).map_err(CryptoStoreError::backend)?;
+
+ // Turn the Vec into a Javascript-side `Array`.
+ // XXX Isn't there a way to do this that *doesn't* involve going via a JSON
+ // string?
+ Ok(JsValue::from_serde(&value)?)
+ } else {
+ // Turn the rust-side struct into a JS-side `Object`.
+ Ok(JsValue::from_serde(&value)?)
+ }
+ }
+
+ /// Encode the value for storage as a value in indexeddb.
+ ///
+ /// This is the same algorithm as [`serialize_value`], but stops short of
+ /// encoding the resultant byte vector in a JsValue.
+ ///
+ /// Returns a byte vector which is either the JSON serialisation of the
+ /// value, or an encrypted version thereof.
+ pub fn serialize_value_as_bytes(
+ &self,
+ value: &impl Serialize,
+ ) -> Result, CryptoStoreError> {
+ match &self.store_cipher {
+ Some(cipher) => cipher.encrypt_value(value).map_err(CryptoStoreError::backend),
+ None => serde_json::to_vec(value).map_err(CryptoStoreError::backend),
+ }
+ }
+
+ /// Decode a value that was previously encoded with [`serialize_value`]
+ pub fn deserialize_value(
+ &self,
+ value: JsValue,
+ ) -> Result {
+ if let Some(cipher) = &self.store_cipher {
+ // `value` is a JS-side array containing the byte values. Turn it into a
+ // rust-side Vec.
+ // XXX: Isn't there a way to do this that *doesn't* involve going via a JSON
+ // string?
+ let value: Vec = value.into_serde()?;
+
+ cipher.decrypt_value(&value).map_err(CryptoStoreError::backend)
+ } else {
+ Ok(value.into_serde()?)
+ }
+ }
+
+ /// Decode a value that was previously encoded with
+ /// [`serialize_value_as_bytes`]
+ pub fn deserialize_value_from_bytes(
+ &self,
+ value: &[u8],
+ ) -> Result {
+ if let Some(cipher) = &self.store_cipher {
+ cipher.decrypt_value(value).map_err(CryptoStoreError::backend)
+ } else {
+ serde_json::from_slice(value).map_err(CryptoStoreError::backend)
+ }
+ }
+}
diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/migrations.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations.rs
new file mode 100644
index 00000000000..ef04b630e67
--- /dev/null
+++ b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations.rs
@@ -0,0 +1,138 @@
+// Copyright 2023 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use indexed_db_futures::{prelude::*, web_sys::DomException};
+use tracing::info;
+use wasm_bindgen::JsValue;
+
+use crate::crypto_store::{keys, Result};
+
+/// Open the indexeddb with the given name, upgrading it to the latest version
+/// of the schema if necessary.
+pub async fn open_and_upgrade_db(name: &str) -> Result {
+ let mut db_req: OpenDbRequest = IdbDatabase::open_u32(name, 5)?;
+
+ db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
+ // Even if the web-sys bindings expose the version as a f64, the IndexedDB API
+ // works with an unsigned integer.
+ // See
+ let old_version = evt.old_version() as u32;
+ let new_version = evt.new_version() as u32;
+
+ info!(old_version, new_version, "Upgrading IndexeddbCryptoStore");
+
+ if old_version < 1 {
+ create_stores_for_v1(evt.db())?;
+ }
+
+ if old_version < 2 {
+ create_stores_for_v2(evt.db())?;
+ }
+
+ if old_version < 3 {
+ create_stores_for_v3(evt.db())?;
+ }
+
+ if old_version < 4 {
+ create_stores_for_v4(evt.db())?;
+ }
+
+ if old_version < 5 {
+ create_stores_for_v5(evt.db())?;
+ }
+
+ info!(old_version, new_version, "IndexeddbCryptoStore upgrade complete");
+ Ok(())
+ }));
+
+ db_req.await
+}
+
+fn create_stores_for_v1(db: &IdbDatabase) -> Result<(), DomException> {
+ db.create_object_store(keys::CORE)?;
+ db.create_object_store(keys::SESSION)?;
+
+ db.create_object_store(keys::INBOUND_GROUP_SESSIONS)?;
+ db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
+ db.create_object_store(keys::TRACKED_USERS)?;
+ db.create_object_store(keys::OLM_HASHES)?;
+ db.create_object_store(keys::DEVICES)?;
+
+ db.create_object_store(keys::IDENTITIES)?;
+ db.create_object_store(keys::BACKUP_KEYS)?;
+
+ Ok(())
+}
+
+fn create_stores_for_v2(db: &IdbDatabase) -> Result<(), DomException> {
+ // We changed how we store inbound group sessions, the key used to
+ // be a tuple of `(room_id, sender_key, session_id)` now it's a
+ // tuple of `(room_id, session_id)`
+ //
+ // Let's just drop the whole object store.
+ db.delete_object_store(keys::INBOUND_GROUP_SESSIONS)?;
+ db.create_object_store(keys::INBOUND_GROUP_SESSIONS)?;
+
+ db.create_object_store(keys::ROOM_SETTINGS)?;
+
+ Ok(())
+}
+
+fn create_stores_for_v3(db: &IdbDatabase) -> Result<(), DomException> {
+ // We changed the way we store outbound session.
+ // ShareInfo changed from a struct to an enum with struct variant.
+ // Let's just discard the existing outbounds
+ db.delete_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
+ db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
+
+ // Support for MSC2399 withheld codes
+ db.create_object_store(keys::DIRECT_WITHHELD_INFO)?;
+
+ Ok(())
+}
+
+fn create_stores_for_v4(db: &IdbDatabase) -> Result<(), DomException> {
+ db.create_object_store(keys::SECRETS_INBOX)?;
+ Ok(())
+}
+
+fn create_stores_for_v5(db: &IdbDatabase) -> Result<(), DomException> {
+ // Create a new store for outgoing secret requests
+ let object_store = db.create_object_store(keys::GOSSIP_REQUESTS)?;
+
+ let mut params = IdbIndexParameters::new();
+ params.unique(false);
+ object_store.create_index_with_params(
+ keys::GOSSIP_REQUESTS_UNSENT_INDEX,
+ &IdbKeyPath::str("unsent"),
+ ¶ms,
+ )?;
+
+ let mut params = IdbIndexParameters::new();
+ params.unique(true);
+ object_store.create_index_with_params(
+ keys::GOSSIP_REQUESTS_BY_INFO_INDEX,
+ &IdbKeyPath::str("info"),
+ ¶ms,
+ )?;
+
+ if db.object_store_names().any(|n| n == "outgoing_secret_requests") {
+ // Delete the old store names. We just delete any existing requests.
+ db.delete_object_store("outgoing_secret_requests")?;
+ db.delete_object_store("unsent_secret_requests")?;
+ db.delete_object_store("secret_requests_by_info")?;
+ }
+
+ Ok(())
+}
diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs
similarity index 70%
rename from crates/matrix-sdk-indexeddb/src/crypto_store.rs
rename to crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs
index c3016226aed..ec1553c7d3b 100644
--- a/crates/matrix-sdk-indexeddb/src/crypto_store.rs
+++ b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs
@@ -38,12 +38,15 @@ use ruma::{
events::secret::request::SecretName, DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId,
RoomId, TransactionId, UserId,
};
-use serde::{de::DeserializeOwned, Serialize};
use tokio::sync::Mutex;
use wasm_bindgen::JsValue;
-use web_sys::IdbKeyRange;
-use crate::safe_encode::SafeEncode;
+use crate::crypto_store::{
+ indexeddb_serializer::IndexeddbSerializer, migrations::open_and_upgrade_db,
+};
+
+mod indexeddb_serializer;
+mod migrations;
mod keys {
// stores
@@ -96,8 +99,7 @@ pub struct IndexeddbCryptoStore {
name: String,
pub(crate) inner: IdbDatabase,
- store_cipher: Option>,
-
+ serializer: IndexeddbSerializer,
session_cache: SessionStore,
save_changes_lock: Arc>,
}
@@ -112,7 +114,7 @@ impl std::fmt::Debug for IndexeddbCryptoStore {
#[derive(Debug, thiserror::Error)]
pub enum IndexeddbCryptoStoreError {
#[error(transparent)]
- Json(#[from] serde_json::Error),
+ Serialization(#[from] serde_json::Error),
#[error("DomException {name} ({code}): {message}")]
DomException {
/// DomException code
@@ -136,10 +138,16 @@ impl From for IndexeddbCryptoStoreErr
}
}
+impl From for IndexeddbCryptoStoreError {
+ fn from(e: serde_wasm_bindgen::Error) -> Self {
+ IndexeddbCryptoStoreError::Serialization(serde::de::Error::custom(e.to_string()))
+ }
+}
+
impl From for CryptoStoreError {
fn from(frm: IndexeddbCryptoStoreError) -> CryptoStoreError {
match frm {
- IndexeddbCryptoStoreError::Json(e) => CryptoStoreError::Serialization(e),
+ IndexeddbCryptoStoreError::Serialization(e) => CryptoStoreError::Serialization(e),
IndexeddbCryptoStoreError::CryptoStoreError(e) => e,
_ => CryptoStoreError::backend(frm),
}
@@ -155,104 +163,15 @@ impl IndexeddbCryptoStore {
) -> Result {
let name = format!("{prefix:0}::matrix-sdk-crypto");
- // Open my_db v1
- let mut db_req: OpenDbRequest = IdbDatabase::open_u32(&name, 5)?;
- db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
- // Even if the web-sys bindings expose the version as a f64, the IndexedDB API
- // works with an unsigned integer.
- // See
- let old_version = evt.old_version() as u32;
-
- if old_version < 1 {
- // migrating to version 1
- let db = evt.db();
-
- db.create_object_store(keys::CORE)?;
- db.create_object_store(keys::SESSION)?;
-
- db.create_object_store(keys::INBOUND_GROUP_SESSIONS)?;
- db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
- db.create_object_store(keys::TRACKED_USERS)?;
- db.create_object_store(keys::OLM_HASHES)?;
- db.create_object_store(keys::DEVICES)?;
-
- db.create_object_store(keys::IDENTITIES)?;
- db.create_object_store(keys::BACKUP_KEYS)?;
- }
-
- if old_version < 2 {
- let db = evt.db();
-
- // We changed how we store inbound group sessions, the key used to
- // be a trippled of `(room_id, sender_key, session_id)` now it's a
- // tuple of `(room_id, session_id)`
- //
- // Let's just drop the whole object store.
- db.delete_object_store(keys::INBOUND_GROUP_SESSIONS)?;
- db.create_object_store(keys::INBOUND_GROUP_SESSIONS)?;
- db.create_object_store(keys::ROOM_SETTINGS)?;
- }
-
- if old_version < 3 {
- let db = evt.db();
-
- // We changed the way we store outbound session.
- // ShareInfo changed from a struct to an enum with struct variant.
- // Let's just discard the existing outbounds
- db.delete_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
- db.create_object_store(keys::OUTBOUND_GROUP_SESSIONS)?;
-
- // Support for MSC2399 withheld codes
- db.create_object_store(keys::DIRECT_WITHHELD_INFO)?;
- }
-
- if old_version < 4 {
- let db = evt.db();
-
- db.create_object_store(keys::SECRETS_INBOX)?;
- }
-
- if old_version < 5 {
- let db = evt.db();
-
- // Create a new store for outgoing secret requests
- let object_store = db.create_object_store(keys::GOSSIP_REQUESTS)?;
-
- let mut params = IdbIndexParameters::new();
- params.unique(false);
- object_store.create_index_with_params(
- keys::GOSSIP_REQUESTS_UNSENT_INDEX,
- &IdbKeyPath::str("unsent"),
- ¶ms,
- )?;
-
- let mut params = IdbIndexParameters::new();
- params.unique(true);
- object_store.create_index_with_params(
- keys::GOSSIP_REQUESTS_BY_INFO_INDEX,
- &IdbKeyPath::str("info"),
- ¶ms,
- )?;
-
- if old_version > 0 {
- // we just delete any existing requests.
- db.delete_object_store("outgoing_secret_requests")?;
- db.delete_object_store("unsent_secret_requests")?;
- db.delete_object_store("secret_requests_by_info")?;
- }
- }
-
- Ok(())
- }));
-
- let db: IdbDatabase = db_req.await?;
+ let serializer = IndexeddbSerializer::new(store_cipher);
+ let db = open_and_upgrade_db(&name).await?;
let session_cache = SessionStore::new();
Ok(Self {
name,
session_cache,
inner: db,
- store_cipher,
+ serializer,
static_account: RwLock::new(None),
save_changes_lock: Default::default(),
})
@@ -263,60 +182,6 @@ impl IndexeddbCryptoStore {
IndexeddbCryptoStore::open_with_store_cipher("crypto", None).await
}
- /// Hash the given key securely for the given tablename, using the store
- /// cipher.
- ///
- /// First calls [`SafeEncode::as_encoded_string`]
- /// on the `key` to encode it into a formatted string.
- ///
- /// Then, if a cipher is configured, hashes the formatted key and returns
- /// the hash encoded as unpadded base64.
- ///
- /// If no cipher is configured, just returns the formatted key.
- ///
- /// This is faster than [`serialize_value`] and reliably gives the same
- /// output for the same input, making it suitable for index keys.
- fn encode_key(&self, table_name: &str, key: T) -> JsValue
- where
- T: SafeEncode,
- {
- self.encode_key_as_string(table_name, key).into()
- }
-
- /// Hash the given key securely for the given tablename, using the store
- /// cipher.
- ///
- /// The same as [`encode_key`], but stops short of converting the resulting
- /// base64 string into a JsValue
- fn encode_key_as_string(&self, table_name: &str, key: T) -> String
- where
- T: SafeEncode,
- {
- match &self.store_cipher {
- Some(cipher) => key.as_secure_string(table_name, cipher),
- None => key.as_encoded_string(),
- }
- }
-
- fn encode_to_range(
- &self,
- table_name: &str,
- key: T,
- ) -> Result
- where
- T: SafeEncode,
- {
- match &self.store_cipher {
- Some(cipher) => key.encode_to_range_secure(table_name, cipher),
- None => key.encode_to_range(),
- }
- .map_err(|e| IndexeddbCryptoStoreError::DomException {
- code: 0,
- name: "IdbKeyRangeMakeError".to_owned(),
- message: e,
- })
- }
-
/// Open a new `IndexeddbCryptoStore` with given name and passphrase
pub async fn open_with_passphrase(prefix: &str, passphrase: &str) -> Result {
let name = format!("{prefix:0}::matrix-sdk-crypto-meta");
@@ -382,100 +247,34 @@ impl IndexeddbCryptoStore {
IndexeddbCryptoStore::open_with_store_cipher(name, None).await
}
- /// Encode the value for storage as a value in indexeddb.
- ///
- /// First, serialise the given value as JSON.
- ///
- /// Then, if a store cipher is enabled, encrypt the JSON string using the
- /// configured store cipher, giving a byte array. Then, wrap the byte
- /// array as a `JsValue`.
- ///
- /// If no cipher is enabled, deserialises the JSON string again giving a JS
- /// object.
- fn serialize_value(&self, value: &impl Serialize) -> Result {
- if let Some(cipher) = &self.store_cipher {
- let value = cipher.encrypt_value(value).map_err(CryptoStoreError::backend)?;
-
- Ok(JsValue::from_serde(&value)?)
- } else {
- Ok(JsValue::from_serde(&value)?)
- }
- }
-
- /// Encode the value for storage as a value in indexeddb.
- ///
- /// This is the same algorithm as [`serialize_value`], but stops short of
- /// encoding the resultant byte vector in a JsValue.
- ///
- /// Returns a byte vector which is either the JSON serialisation of the
- /// value, or an encrypted version thereof.
- fn serialize_value_as_bytes(
- &self,
- value: &impl Serialize,
- ) -> Result, CryptoStoreError> {
- match &self.store_cipher {
- Some(cipher) => cipher.encrypt_value(value).map_err(CryptoStoreError::backend),
- None => serde_json::to_vec(value).map_err(CryptoStoreError::backend),
- }
- }
-
- /// Decode a value that was previously encoded with [`serialize_value`]
- fn deserialize_value(
- &self,
- value: JsValue,
- ) -> Result {
- if let Some(cipher) = &self.store_cipher {
- let value: Vec = value.into_serde()?;
- cipher.decrypt_value(&value).map_err(CryptoStoreError::backend)
- } else {
- Ok(value.into_serde()?)
- }
- }
-
- /// Decode a value that was previously encoded with
- /// [`serialize_value_as_bytes`]
- fn deserialize_value_from_bytes(
- &self,
- value: &[u8],
- ) -> Result {
- if let Some(cipher) = &self.store_cipher {
- cipher.decrypt_value(value).map_err(CryptoStoreError::backend)
- } else {
- serde_json::from_slice(value).map_err(CryptoStoreError::backend)
- }
- }
-
fn get_static_account(&self) -> Option {
self.static_account.read().unwrap().clone()
}
/// Transform a [`GossipRequest`] into a `JsValue` holding a
- /// [`GossipRequestIndexedDbObject`], ready for storing
- fn serialize_gossip_request(
- &self,
- gossip_request: &GossipRequest,
- ) -> Result {
+ /// [`GossipRequestIndexedDbObject`], ready for storing.
+ fn serialize_gossip_request(&self, gossip_request: &GossipRequest) -> Result {
let obj = GossipRequestIndexedDbObject {
// hash the info as a key so that it can be used in index lookups.
- info: self.encode_key_as_string(keys::GOSSIP_REQUESTS, gossip_request.info.as_key()),
+ info: self
+ .serializer
+ .encode_key_as_string(keys::GOSSIP_REQUESTS, gossip_request.info.as_key()),
// serialize and encrypt the data about the request
- request: self.serialize_value_as_bytes(gossip_request)?,
+ request: self.serializer.serialize_value_as_bytes(gossip_request)?,
- unsent: if gossip_request.sent_out { None } else { Some(1) },
+ unsent: !gossip_request.sent_out,
};
- Ok(obj.try_into()?)
+ Ok(serde_wasm_bindgen::to_value(&obj)?)
}
/// Transform a JsValue holding a [`GossipRequestIndexedDbObject`] back into
- /// a [`GossipRequest`]
- fn deserialize_gossip_request(
- &self,
- stored_request: JsValue,
- ) -> Result {
- let idb_object: GossipRequestIndexedDbObject = stored_request.try_into()?;
- self.deserialize_value_from_bytes(&idb_object.request)
+ /// a [`GossipRequest`].
+ fn deserialize_gossip_request(&self, stored_request: JsValue) -> Result {
+ let idb_object: GossipRequestIndexedDbObject =
+ serde_wasm_bindgen::from_value(stored_request)?;
+ Ok(self.serializer.deserialize_value_from_bytes(&idb_object.request)?)
}
}
@@ -540,7 +339,7 @@ impl_crypto_store! {
if let Some(a) = &account_pickle {
tx.object_store(keys::CORE)?
- .put_key_val(&JsValue::from_str(keys::ACCOUNT), &self.serialize_value(&a)?)?;
+ .put_key_val(&JsValue::from_str(keys::ACCOUNT), &self.serializer.serialize_value(&a)?)?;
}
tx.await.into_result()?;
@@ -602,27 +401,27 @@ impl_crypto_store! {
if let Some(next_batch) = changes.next_batch_token {
tx.object_store(keys::CORE)?.put_key_val(
&JsValue::from_str(keys::NEXT_BATCH_TOKEN),
- &self.serialize_value(&next_batch)?
+ &self.serializer.serialize_value(&next_batch)?
)?;
}
if let Some(i) = &private_identity_pickle {
tx.object_store(keys::CORE)?.put_key_val(
&JsValue::from_str(keys::PRIVATE_IDENTITY),
- &self.serialize_value(i)?,
+ &self.serializer.serialize_value(i)?,
)?;
}
if let Some(a) = &decryption_key_pickle {
tx.object_store(keys::BACKUP_KEYS)?.put_key_val(
&JsValue::from_str(keys::RECOVERY_KEY_V1),
- &self.serialize_value(&a)?,
+ &self.serializer.serialize_value(&a)?,
)?;
}
if let Some(a) = &backup_version {
tx.object_store(keys::BACKUP_KEYS)?
- .put_key_val(&JsValue::from_str(keys::BACKUP_KEY_V1), &self.serialize_value(&a)?)?;
+ .put_key_val(&JsValue::from_str(keys::BACKUP_KEY_V1), &self.serializer.serialize_value(&a)?)?;
}
if !changes.sessions.is_empty() {
@@ -633,9 +432,9 @@ impl_crypto_store! {
let session_id = session.session_id();
let pickle = session.pickle().await;
- let key = self.encode_key(keys::SESSION, (&sender_key, session_id));
+ let key = self.serializer.encode_key(keys::SESSION, (&sender_key, session_id));
- sessions.put_key_val(&key, &self.serialize_value(&pickle)?)?;
+ sessions.put_key_val(&key, &self.serializer.serialize_value(&pickle)?)?;
}
}
@@ -645,10 +444,10 @@ impl_crypto_store! {
for session in changes.inbound_group_sessions {
let room_id = session.room_id();
let session_id = session.session_id();
- let key = self.encode_key(keys::INBOUND_GROUP_SESSIONS, (room_id, session_id));
+ let key = self.serializer.encode_key(keys::INBOUND_GROUP_SESSIONS, (room_id, session_id));
let pickle = session.pickle().await;
- sessions.put_key_val(&key, &self.serialize_value(&pickle)?)?;
+ sessions.put_key_val(&key, &self.serializer.serialize_value(&pickle)?)?;
}
}
@@ -659,8 +458,8 @@ impl_crypto_store! {
let room_id = session.room_id();
let pickle = session.pickle().await;
sessions.put_key_val(
- &self.encode_key(keys::OUTBOUND_GROUP_SESSIONS, room_id),
- &self.serialize_value(&pickle)?,
+ &self.serializer.encode_key(keys::OUTBOUND_GROUP_SESSIONS, room_id),
+ &self.serializer.serialize_value(&pickle)?,
)?;
}
}
@@ -675,8 +474,8 @@ impl_crypto_store! {
if !device_changes.new.is_empty() || !device_changes.changed.is_empty() {
let device_store = tx.object_store(keys::DEVICES)?;
for device in device_changes.new.iter().chain(&device_changes.changed) {
- let key = self.encode_key(keys::DEVICES, (device.user_id(), device.device_id()));
- let device = self.serialize_value(&device)?;
+ let key = self.serializer.encode_key(keys::DEVICES, (device.user_id(), device.device_id()));
+ let device = self.serializer.serialize_value(&device)?;
device_store.put_key_val(&key, &device)?;
}
@@ -686,7 +485,7 @@ impl_crypto_store! {
let device_store = tx.object_store(keys::DEVICES)?;
for device in &device_changes.deleted {
- let key = self.encode_key(keys::DEVICES, (device.user_id(), device.device_id()));
+ let key = self.serializer.encode_key(keys::DEVICES, (device.user_id(), device.device_id()));
device_store.delete(&key)?;
}
}
@@ -695,8 +494,8 @@ impl_crypto_store! {
let identities = tx.object_store(keys::IDENTITIES)?;
for identity in identity_changes.changed.iter().chain(&identity_changes.new) {
identities.put_key_val(
- &self.encode_key(keys::IDENTITIES, identity.user_id()),
- &self.serialize_value(&identity)?,
+ &self.serializer.encode_key(keys::IDENTITIES, identity.user_id()),
+ &self.serializer.serialize_value(&identity)?,
)?;
}
}
@@ -705,7 +504,7 @@ impl_crypto_store! {
let hashes = tx.object_store(keys::OLM_HASHES)?;
for hash in &olm_hashes {
hashes.put_key_val(
- &self.encode_key(keys::OLM_HASHES, (&hash.sender_key, &hash.hash)),
+ &self.serializer.encode_key(keys::OLM_HASHES, (&hash.sender_key, &hash.hash)),
&JsValue::TRUE,
)?;
}
@@ -715,7 +514,7 @@ impl_crypto_store! {
let gossip_requests = tx.object_store(keys::GOSSIP_REQUESTS)?;
for gossip_request in &key_requests {
- let key_request_id = self.encode_key(keys::GOSSIP_REQUESTS, gossip_request.request_id.as_str());
+ let key_request_id = self.serializer.encode_key(keys::GOSSIP_REQUESTS, gossip_request.request_id.as_str());
let key_request_value = self.serialize_gossip_request(gossip_request)?;
gossip_requests.put_key_val_owned(
key_request_id,
@@ -730,8 +529,8 @@ impl_crypto_store! {
for (room_id, data) in withheld_session_info {
for (session_id, event) in data {
- let key = self.encode_key(keys::DIRECT_WITHHELD_INFO, (session_id, &room_id));
- withhelds.put_key_val(&key, &self.serialize_value(&event)?)?;
+ let key = self.serializer.encode_key(keys::DIRECT_WITHHELD_INFO, (session_id, &room_id));
+ withhelds.put_key_val(&key, &self.serializer.serialize_value(&event)?)?;
}
}
}
@@ -740,8 +539,8 @@ impl_crypto_store! {
let settings_store = tx.object_store(keys::ROOM_SETTINGS)?;
for (room_id, settings) in &room_settings_changes {
- let key = self.encode_key(keys::ROOM_SETTINGS, room_id);
- let value = self.serialize_value(&settings)?;
+ let key = self.serializer.encode_key(keys::ROOM_SETTINGS, room_id);
+ let value = self.serializer.serialize_value(&settings)?;
settings_store.put_key_val(&key, &value)?;
}
}
@@ -750,8 +549,8 @@ impl_crypto_store! {
let secrets_store = tx.object_store(keys::SECRETS_INBOX)?;
for secret in changes.secrets {
- let key = self.encode_key(keys::SECRETS_INBOX, (secret.secret_name.as_str(), secret.event.content.request_id.as_str()));
- let value = self.serialize_value(&secret)?;
+ let key = self.serializer.encode_key(keys::SECRETS_INBOX, (secret.secret_name.as_str(), secret.event.content.request_id.as_str()));
+ let value = self.serializer.serialize_value(&secret)?;
secrets_store.put_key_val(&key, &value)?;
}
@@ -799,14 +598,14 @@ impl_crypto_store! {
IdbTransactionMode::Readonly,
)?
.object_store(keys::OUTBOUND_GROUP_SESSIONS)?
- .get(&self.encode_key(keys::OUTBOUND_GROUP_SESSIONS, room_id))?
+ .get(&self.serializer.encode_key(keys::OUTBOUND_GROUP_SESSIONS, room_id))?
.await?
{
Ok(Some(
OutboundGroupSession::from_pickle(
account_info.device_id,
account_info.identity_keys,
- self.deserialize_value(value)?,
+ self.serializer.deserialize_value(value)?,
)
.map_err(CryptoStoreError::from)?,
))
@@ -819,15 +618,15 @@ impl_crypto_store! {
&self,
request_id: &TransactionId,
) -> Result