From 8b89023aff9cc61ed178c977e7fc23efffbc2d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Mar 2022 14:47:49 +0100 Subject: [PATCH] feat(indxeddb): Use the new store-cipher to encrypt our values --- crates/matrix-sdk-indexeddb/Cargo.toml | 1 + .../matrix-sdk-indexeddb/src/cryptostore.rs | 131 +++++++++++++----- 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/Cargo.toml b/crates/matrix-sdk-indexeddb/Cargo.toml index 06c7b1168..1f373c358 100644 --- a/crates/matrix-sdk-indexeddb/Cargo.toml +++ b/crates/matrix-sdk-indexeddb/Cargo.toml @@ -14,6 +14,7 @@ default-target = "wasm32-unknown-unknown" matrix-sdk-base = { path = "../matrix-sdk-base", features = ["store_key"] } 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" diff --git a/crates/matrix-sdk-indexeddb/src/cryptostore.rs b/crates/matrix-sdk-indexeddb/src/cryptostore.rs index af34b6fa8..6c9146562 100644 --- a/crates/matrix-sdk-indexeddb/src/cryptostore.rs +++ b/crates/matrix-sdk-indexeddb/src/cryptostore.rs @@ -35,6 +35,8 @@ use matrix_sdk_crypto::{ }, GossipRequest, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyUserIdentities, SecretInfo, }; +use matrix_sdk_store_encryption::StoreCipher; +use serde::{Deserialize, Serialize}; use wasm_bindgen::JsValue; use crate::safe_encode::SafeEncode; @@ -60,8 +62,7 @@ mod KEYS { pub const SECRET_REQUESTS_BY_INFO: &str = "secret_requests_by_info"; // KEYS - #[allow(dead_code)] - pub const PICKLE_KEY: &str = "pickle_key"; + pub const STORE_CIPHER: &str = "store_cipher"; pub const ACCOUNT: &str = "account"; pub const PRIVATE_IDENTITY: &str = "private_identity"; } @@ -72,6 +73,8 @@ pub struct IndexeddbStore { name: String, pub(crate) inner: IdbDatabase, + store_cipher: Arc>, + session_cache: SessionStore, tracked_users_cache: Arc>>, users_for_key_query_cache: Arc>>, @@ -130,7 +133,7 @@ pub struct AccountInfo { } impl IndexeddbStore { - async fn open_helper(prefix: String) -> Result { + async fn open_helper(prefix: String, store_cipher: Option) -> Result { let name = format!("{:0}::matrix-sdk-crypto", prefix); // Open my_db v1 @@ -164,6 +167,7 @@ impl IndexeddbStore { name, session_cache, inner: db, + store_cipher: store_cipher.into(), account_info: RwLock::new(None).into(), tracked_users_cache: DashSet::new().into(), users_for_key_query_cache: DashSet::new().into(), @@ -172,11 +176,11 @@ impl IndexeddbStore { /// Open a new IndexeddbStore with default name and no passphrase pub async fn open() -> Result { - IndexeddbStore::open_helper("crypto".to_owned()).await + IndexeddbStore::open_helper("crypto".to_owned(), None).await } /// Open a new IndexeddbStore with given name and passphrase - pub async fn open_with_passphrase(prefix: String, _passphrase: &str) -> Result { + pub async fn open_with_passphrase(prefix: String, passphrase: &str) -> Result { let name = format!("{:0}::matrix-sdk-crypto-meta", prefix); let mut db_req: OpenDbRequest = IdbDatabase::open_f64(&name, 1.0)?; @@ -190,14 +194,70 @@ impl IndexeddbStore { Ok(()) })); - let _: IdbDatabase = db_req.into_future().await?; + let db: IdbDatabase = db_req.into_future().await?; - IndexeddbStore::open_helper(prefix).await + let tx: IdbTransaction<'_> = + db.transaction_on_one_with_mode("matrix-sdk-crypto", IdbTransactionMode::Readonly)?; + let ob = tx.object_store("matrix-sdk-crypto")?; + + let store_cipher: Option> = ob + .get(&JsValue::from_str(KEYS::STORE_CIPHER))? + .await? + .map(|k| k.into_serde()) + .transpose()?; + + let store_cipher = match store_cipher { + Some(key) => StoreCipher::import(passphrase, &key) + .map_err(|_| CryptoStoreError::UnpicklingError)?, + None => { + let key = StoreCipher::new().map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + let encrypted = + key.export(passphrase).map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + + let tx: IdbTransaction<'_> = db.transaction_on_one_with_mode( + "matrix-sdk-crypto", + IdbTransactionMode::Readwrite, + )?; + let ob = tx.object_store("matrix-sdk-crypto")?; + + ob.put_key_val( + &JsValue::from_str(KEYS::STORE_CIPHER), + &JsValue::from_serde(&encrypted)?, + )?; + tx.await.into_result()?; + key + } + }; + + IndexeddbStore::open_helper(prefix, Some(store_cipher)).await } /// Open a new IndexeddbStore with given name and no passphrase pub async fn open_with_name(name: String) -> Result { - IndexeddbStore::open_helper(name).await + IndexeddbStore::open_helper(name, None).await + } + + fn serialize_value(&self, value: &impl Serialize) -> Result { + if let Some(key) = &*self.store_cipher { + let value = + key.encrypt_value(value).map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + + Ok(JsValue::from_serde(&value)?) + } else { + Ok(JsValue::from_serde(&value)?) + } + } + + fn deserialize_value Deserialize<'b>>( + &self, + value: JsValue, + ) -> Result { + if let Some(key) = &*self.store_cipher { + let value: Vec = value.into_serde()?; + key.decrypt_value(&value).map_err(|e| CryptoStoreError::Backend(anyhow!(e))) + } else { + Ok(value.into_serde()?) + } } fn get_account_info(&self) -> Option { @@ -250,13 +310,13 @@ impl IndexeddbStore { if let Some(a) = &account_pickle { tx.object_store(KEYS::CORE)? - .put_key_val(&JsValue::from_str(KEYS::ACCOUNT), &JsValue::from_serde(&a)?)?; + .put_key_val(&JsValue::from_str(KEYS::ACCOUNT), &self.serialize_value(&a)?)?; } if let Some(i) = &private_identity_pickle { tx.object_store(KEYS::CORE)?.put_key_val( &JsValue::from_str(KEYS::PRIVATE_IDENTITY), - &JsValue::from_serde(i)?, + &self.serialize_value(i)?, )?; } @@ -270,7 +330,7 @@ impl IndexeddbStore { let pickle = session.pickle().await; let key = (sender_key, session_id).encode(); - sessions.put_key_val(&key, &JsValue::from_serde(&pickle)?)?; + sessions.put_key_val(&key, &self.serialize_value(&pickle)?)?; } } @@ -284,7 +344,7 @@ impl IndexeddbStore { let key = (room_id, sender_key, session_id).encode(); let pickle = session.pickle().await; - sessions.put_key_val(&key, &JsValue::from_serde(&pickle)?)?; + sessions.put_key_val(&key, &self.serialize_value(&pickle)?)?; } } @@ -294,7 +354,7 @@ impl IndexeddbStore { for session in changes.outbound_group_sessions { let room_id = session.room_id(); let pickle = session.pickle().await; - sessions.put_key_val(&room_id.encode(), &JsValue::from_serde(&pickle)?)?; + sessions.put_key_val(&room_id.encode(), &self.serialize_value(&pickle)?)?; } } @@ -307,7 +367,7 @@ impl IndexeddbStore { let device_store = tx.object_store(KEYS::DEVICES)?; for device in device_changes.new.iter().chain(&device_changes.changed) { let key = (device.user_id(), device.device_id()).encode(); - let device = JsValue::from_serde(&device)?; + let device = self.serialize_value(&device)?; device_store.put_key_val(&key, &device)?; } @@ -326,7 +386,7 @@ impl IndexeddbStore { let identities = tx.object_store(KEYS::IDENTITIES)?; for identity in identity_changes.changed.iter().chain(&identity_changes.new) { identities - .put_key_val(&identity.user_id().encode(), &JsValue::from_serde(&identity)?)?; + .put_key_val(&identity.user_id().encode(), &self.serialize_value(&identity)?)?; } } @@ -349,11 +409,11 @@ impl IndexeddbStore { if key_request.sent_out { unsent_secret_requests.delete(&key_request_id)?; outgoing_secret_requests - .put_key_val(&key_request_id, &JsValue::from_serde(&key_request)?)?; + .put_key_val(&key_request_id, &self.serialize_value(&key_request)?)?; } else { outgoing_secret_requests.delete(&key_request_id)?; unsent_secret_requests - .put_key_val(&key_request_id, &JsValue::from_serde(&key_request)?)?; + .put_key_val(&key_request_id, &self.serialize_value(&key_request)?)?; } } } @@ -410,7 +470,7 @@ impl IndexeddbStore { OutboundGroupSession::from_pickle( account_info.device_id, account_info.identity_keys, - value.into_serde()?, + self.deserialize_value(value)?, ) .map_err(CryptoStoreError::from)?, )) @@ -428,7 +488,7 @@ impl IndexeddbStore { .object_store(KEYS::OUTGOING_SECRET_REQUESTS)? .get(&jskey)? .await? - .map(|i| i.into_serde()) + .map(|i| self.deserialize_value(i)) .transpose()?; Ok(match request { @@ -436,7 +496,7 @@ impl IndexeddbStore { .object_store(KEYS::UNSENT_SECRET_REQUESTS)? .get(&jskey)? .await? - .map(|i| i.into_serde()) + .map(|i| self.deserialize_value(i)) .transpose()?, Some(request) => Some(request), }) @@ -452,8 +512,9 @@ impl IndexeddbStore { { self.load_tracked_users().await?; - let account = ReadOnlyAccount::from_pickle(pickle.into_serde()?) - .map_err(CryptoStoreError::from)?; + let pickle = self.deserialize_value(pickle)?; + + let account = ReadOnlyAccount::from_pickle(pickle).map_err(CryptoStoreError::from)?; let account_info = AccountInfo { user_id: account.user_id.clone(), @@ -477,8 +538,10 @@ impl IndexeddbStore { .get(&JsValue::from_str(KEYS::PRIVATE_IDENTITY))? .await? { + let pickle = self.deserialize_value(pickle)?; + Ok(Some( - PrivateCrossSigningIdentity::from_pickle(pickle.into_serde()?) + PrivateCrossSigningIdentity::from_pickle(pickle) .await .map_err(|_| CryptoStoreError::UnpicklingError)?, )) @@ -504,7 +567,7 @@ impl IndexeddbStore { .get_all_with_key(&range)? .await? .iter() - .filter_map(|f| match f.into_serde() { + .filter_map(|f| match self.deserialize_value(f) { Ok(p) => Some(Session::from_pickle( account_info.user_id.clone(), account_info.device_id.clone(), @@ -538,10 +601,8 @@ impl IndexeddbStore { .get(&key)? .await? { - Ok(Some( - InboundGroupSession::from_pickle(pickle.into_serde()?) - .map_err(CryptoStoreError::from)?, - )) + let pickle = self.deserialize_value(pickle)?; + Ok(Some(InboundGroupSession::from_pickle(pickle).map_err(CryptoStoreError::from)?)) } else { Ok(None) } @@ -558,7 +619,7 @@ impl IndexeddbStore { .get_all()? .await? .iter() - .filter_map(|i| i.into_serde().ok()) + .filter_map(|i| self.deserialize_value(i).ok()) .filter_map(|p| InboundGroupSession::from_pickle(p).ok()) .collect()) } @@ -650,7 +711,7 @@ impl IndexeddbStore { .object_store(KEYS::DEVICES)? .get(&key)? .await? - .map(|i| i.into_serde()) + .map(|i| self.deserialize_value(i)) .transpose()?) } @@ -671,7 +732,7 @@ impl IndexeddbStore { .await? .iter() .filter_map(|d| { - let d: ReadOnlyDevice = d.into_serde().ok()?; + let d: ReadOnlyDevice = self.deserialize_value(d).ok()?; Some((d.device_id().to_owned(), d)) }) .collect::>()) @@ -684,7 +745,7 @@ impl IndexeddbStore { .object_store(KEYS::IDENTITIES)? .get(&user_id.encode())? .await? - .map(|i| i.into_serde()) + .map(|i| self.deserialize_value(i)) .transpose()?) } @@ -730,7 +791,7 @@ impl IndexeddbStore { .get_all()? .await? .iter() - .filter_map(|i| i.into_serde().ok()) + .filter_map(|i| self.deserialize_value(i).ok()) .collect()) } @@ -747,7 +808,7 @@ impl IndexeddbStore { .object_store(KEYS::OUTGOING_SECRET_REQUESTS)? .get(&jskey)? .await? - .map(|i| i.into_serde()) + .map(|i| self.deserialize_value(i)) .transpose()?; let request = match request { @@ -755,7 +816,7 @@ impl IndexeddbStore { .object_store(KEYS::UNSENT_SECRET_REQUESTS)? .get(&jskey)? .await? - .map(|i| i.into_serde()) + .map(|i| self.deserialize_value(i)) .transpose()?, Some(request) => Some(request), };