mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-16 20:49:05 -04:00
feat(indxeddb): Use the new store-cipher to encrypt our values
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
@@ -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<Option<StoreCipher>>,
|
||||
|
||||
session_cache: SessionStore,
|
||||
tracked_users_cache: Arc<DashSet<Box<UserId>>>,
|
||||
users_for_key_query_cache: Arc<DashSet<Box<UserId>>>,
|
||||
@@ -130,7 +133,7 @@ pub struct AccountInfo {
|
||||
}
|
||||
|
||||
impl IndexeddbStore {
|
||||
async fn open_helper(prefix: String) -> Result<Self> {
|
||||
async fn open_helper(prefix: String, store_cipher: Option<StoreCipher>) -> Result<Self> {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
pub async fn open_with_passphrase(prefix: String, passphrase: &str) -> Result<Self> {
|
||||
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<Vec<u8>> = 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<Self> {
|
||||
IndexeddbStore::open_helper(name).await
|
||||
IndexeddbStore::open_helper(name, None).await
|
||||
}
|
||||
|
||||
fn serialize_value(&self, value: &impl Serialize) -> Result<JsValue, CryptoStoreError> {
|
||||
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<T: for<'b> Deserialize<'b>>(
|
||||
&self,
|
||||
value: JsValue,
|
||||
) -> Result<T, CryptoStoreError> {
|
||||
if let Some(key) = &*self.store_cipher {
|
||||
let value: Vec<u8> = 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<AccountInfo> {
|
||||
@@ -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::<HashMap<_, _>>())
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user