diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs index 77a64977f..8ec4f5e3e 100644 --- a/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs +++ b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/mod.rs @@ -25,6 +25,7 @@ use crate::{ mod old_keys; mod v0_to_v5; +mod v10_to_v11; mod v5_to_v7; mod v7; mod v7_to_v8; @@ -106,8 +107,12 @@ pub async fn open_and_upgrade_db( v8_to_v10::schema_delete(name).await?; } + if old_version < 11 { + v10_to_v11::data_migrate(name, serializer).await?; + } + // Open and return the DB (we know it's at the latest version) - Ok(IdbDatabase::open_u32(name, 10)?.await?) + Ok(IdbDatabase::open_u32(name, 11)?.await?) } async fn db_version(name: &str) -> Result { @@ -183,7 +188,10 @@ mod tests { use super::{v0_to_v5, v7::InboundGroupSessionIndexedDbObject2}; use crate::{ - crypto_store::{keys, migrations::*, InboundGroupSessionIndexedDbObject}, + crypto_store::{ + indexeddb_serializer::MaybeEncrypted, keys, migrations::*, + InboundGroupSessionIndexedDbObject, + }, IndexeddbCryptoStore, }; diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/old_keys.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/old_keys.rs index bd2383b1f..1a82a1454 100644 --- a/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/old_keys.rs +++ b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/old_keys.rs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(doc)] +use crate::crypto_store::keys::BACKUP_VERSION_V1; + /// Old format of the `inbound_group_sessions` store which lacked indexes or /// a sensible structure pub const INBOUND_GROUP_SESSIONS_V1: &str = "inbound_group_sessions"; @@ -20,3 +23,6 @@ pub const INBOUND_GROUP_SESSIONS_V1: &str = "inbound_group_sessions"; /// JSON-encoding and arrays of ints instead of base64. /// Also lacked the `backed_up_to` property+index. pub const INBOUND_GROUP_SESSIONS_V2: &str = "inbound_group_sessions2"; + +/// An old name for [`BACKUP_VERSION_V1`]. +pub const BACKUP_KEY_V1: &str = "backup_key_v1"; diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/v10_to_v11.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/v10_to_v11.rs new file mode 100644 index 000000000..7bb94ffd3 --- /dev/null +++ b/crates/matrix-sdk-indexeddb/src/crypto_store/migrations/v10_to_v11.rs @@ -0,0 +1,54 @@ +// Copyright 2024 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. + +//! Migration code that moves from `backup_keys.backup_key_v1` to +//! `backup_keys.backup_version_v1`, switching to a new serialization format. + +use indexed_db_futures::IdbQuerySource; +use wasm_bindgen::JsValue; +use web_sys::IdbTransactionMode; + +use crate::crypto_store::{ + indexeddb_serializer::IndexeddbSerializer, + keys, + migrations::{old_keys, MigrationDb}, +}; + +/// Migrate data from `backup_keys.backup_key_v1` to +/// `backup_keys.backup_version_v1`. +pub(crate) async fn data_migrate( + name: &str, + serializer: &IndexeddbSerializer, +) -> crate::crypto_store::Result<()> { + let db = MigrationDb::new(name, 11).await?; + let txn = db.transaction_on_one_with_mode(keys::BACKUP_KEYS, IdbTransactionMode::Readwrite)?; + let store = txn.object_store(keys::BACKUP_KEYS)?; + + let bv = store.get(&JsValue::from_str(old_keys::BACKUP_KEY_V1))?.await?; + + let Some(bv) = bv else { + return Ok(()); + }; + + // backup_key_v1 was only ever serialized with the legacy format. Also, it's a + // string, so if we use `deserialize_value` on it, it will be incorrectly + // handled as a new-format object. + let bv: String = serializer.deserialize_legacy_value(bv)?; + + // Re-serialize as new format, then store in the new field. + let serialized = serializer.serialize_value(&bv)?; + store.put_key_val(&JsValue::from_str(keys::BACKUP_VERSION_V1), &serialized)?.await?; + store.delete(&JsValue::from_str(old_keys::BACKUP_KEY_V1))?.await?; + Ok(()) +} diff --git a/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs index 8c2f37195..3bcd3fce9 100644 --- a/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs +++ b/crates/matrix-sdk-indexeddb/src/crypto_store/mod.rs @@ -90,7 +90,11 @@ mod keys { // backup v1 pub const BACKUP_KEYS: &str = "backup_keys"; - pub const BACKUP_KEY_V1: &str = "backup_key_v1"; + + /// Indexeddb key for the key backup version that [`RECOVERY_KEY_V1`] + /// corresponds to. + pub const BACKUP_VERSION_V1: &str = "backup_version_v1"; + /// Indexeddb key for the backup decryption key. /// /// Known, for historical reasons, as the recovery key. Not to be confused @@ -479,9 +483,10 @@ impl IndexeddbCryptoStore { } if let Some(a) = &backup_version { - indexeddb_changes - .get(keys::BACKUP_KEYS) - .put(JsValue::from_str(keys::BACKUP_KEY_V1), self.serializer.serialize_value(&a)?); + indexeddb_changes.get(keys::BACKUP_KEYS).put( + JsValue::from_str(keys::BACKUP_VERSION_V1), + self.serializer.serialize_value(&a)?, + ); } if !changes.sessions.is_empty() { @@ -1208,7 +1213,7 @@ impl_crypto_store! { let store = tx.object_store(keys::BACKUP_KEYS)?; let backup_version = store - .get(&JsValue::from_str(keys::BACKUP_KEY_V1))? + .get(&JsValue::from_str(keys::BACKUP_VERSION_V1))? .await? .map(|i| self.serializer.deserialize_value(i)) .transpose()?;