feat(indxeddb): Use the new store-cipher to encrypt our values

This commit is contained in:
Damir Jelić
2022-03-17 14:47:49 +01:00
parent 75de41bb2e
commit 8b89023aff
2 changed files with 97 additions and 35 deletions

View File

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

View File

@@ -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),
};