diff --git a/crates/matrix-sdk-sled/Cargo.toml b/crates/matrix-sdk-sled/Cargo.toml index 1b1c4e938..6bff143e2 100644 --- a/crates/matrix-sdk-sled/Cargo.toml +++ b/crates/matrix-sdk-sled/Cargo.toml @@ -27,6 +27,7 @@ futures-util = { version = "0.3.15", default-features = false } matrix-sdk-base = { path = "../matrix-sdk-base", features = ["store_key"] } matrix-sdk-common = { path = "../matrix-sdk-common" } matrix-sdk-crypto = { path = "../matrix-sdk-crypto", optional = true } +matrix-sdk-store-encryption = { path = "../matrix-sdk-store-encryption" } async-stream = "0.3.2" serde = "1" serde_json = "1.0.64" diff --git a/crates/matrix-sdk-sled/src/cryptostore.rs b/crates/matrix-sdk-sled/src/cryptostore.rs index b71c8c0b7..07ede4a84 100644 --- a/crates/matrix-sdk-sled/src/cryptostore.rs +++ b/crates/matrix-sdk-sled/src/cryptostore.rs @@ -41,6 +41,7 @@ use matrix_sdk_crypto::{ }, GossipRequest, LocalTrust, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyUserIdentities, SecretInfo, }; +use matrix_sdk_store_encryption::StoreCipher; use serde::{Deserialize, Serialize}; pub use sled::Error; use sled::{ @@ -51,12 +52,19 @@ use tracing::debug; use super::OpenStoreError; -/// This needs to be 32 bytes long since AES-GCM requires it, otherwise we will -/// panic once we try to pickle a Signing object. -#[cfg(feature = "backups_v1")] -const DEFAULT_PICKLE: &str = "DEFAULT_PICKLE_PASSPHRASE_123456"; const DATABASE_VERSION: u8 = 3; +// Table names that are used to derive a separate key for each tree. This ensure +// that user ids encoded for different trees won't end up as the same byte +// sequence. This prevents corelation attacks on our tree metadata. +const DEVICE_TABLE_NAME: &str = "crypto-store-devices"; +const IDENTITIES_TABLE_NAME: &str = "crypto-store-identities"; +const SESSIONS_TABLE_NAME: &str = "crypto-store-sessions"; +const INBOUND_GROUP_TABLE_NAME: &str = "crypto-store-inbound-group-sessions"; +const OUTBOUND_GROUP_TABLE_NAME: &str = "crypto-store-outbound-group-sessions"; +const SECRET_REQUEST_BY_INFO_TABLE: &str = "crypto-store-secret-request-by-info"; +const TRACKED_USERS_TABLE: &str = "crypto-store-secret-tracked-users"; + trait EncodeKey { const SEPARATOR: u8 = 0xff; fn encode(&self) -> Vec; @@ -123,6 +131,133 @@ impl EncodeKey for ReadOnlyDevice { } } +impl EncodeSecureKey for (&UserId, &DeviceId) { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let user_id = store_cipher.hash_key(table_name, self.0.as_bytes()); + let device_id = store_cipher.hash_key(table_name, self.1.as_bytes()); + + (user_id, device_id).encode() + } +} + +impl EncodeSecureKey for SecretInfo { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + match self { + SecretInfo::KeyRequest(k) => k.encode_secure(table_name, store_cipher), + SecretInfo::SecretRequest(s) => s.encode_secure(table_name, store_cipher), + } + } +} + +impl EncodeSecureKey for SecretName { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let name = store_cipher.hash_key(table_name, self.as_ref().as_bytes()); + + [name.as_slice(), &[Self::SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for RequestedKeyInfo { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let room_id = store_cipher.hash_key(table_name, self.room_id.as_bytes()); + let sender_key = store_cipher.hash_key(table_name, self.sender_key.as_bytes()); + let algorithm = store_cipher.hash_key(table_name, self.algorithm.as_ref().as_bytes()); + let session_id = store_cipher.hash_key(table_name, self.session_id.as_bytes()); + + [ + room_id.as_slice(), + &[Self::SEPARATOR], + sender_key.as_slice(), + &[Self::SEPARATOR], + algorithm.as_slice(), + &[Self::SEPARATOR], + session_id.as_slice(), + &[Self::SEPARATOR], + ] + .concat() + } +} + +impl EncodeSecureKey for UserId { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let user_id = store_cipher.hash_key(table_name, self.as_bytes()); + + [user_id.as_slice(), &[0xff]].concat() + } +} + +impl EncodeSecureKey for ReadOnlyDevice { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + (self.user_id(), self.device_id()).encode_secure(table_name, store_cipher) + } +} + +impl EncodeKey for Session { + fn encode(&self) -> Vec { + let sender_key = self.sender_key(); + let session_id = self.session_id(); + + [sender_key.as_bytes(), &[Self::SEPARATOR], session_id.as_bytes(), &[Self::SEPARATOR]] + .concat() + } +} + +impl EncodeSecureKey for str { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let key = store_cipher.hash_key(table_name, self.as_bytes()); + [key.as_slice(), &[Self::SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for OutboundGroupSession { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + self.room_id().encode_secure(table_name, store_cipher) + } +} + +impl EncodeSecureKey for RoomId { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let room_id = store_cipher.hash_key(table_name, self.as_bytes()); + + [room_id.as_slice(), &[Self::SEPARATOR]].concat() + } +} + +impl EncodeSecureKey for InboundGroupSession { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + (self.room_id(), self.sender_key(), self.session_id()) + .encode_secure(table_name, store_cipher) + } +} + +impl EncodeSecureKey for (&RoomId, &str, &str) { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let first = store_cipher.hash_key(table_name, self.0.as_bytes()); + let second = store_cipher.hash_key(table_name, self.1.as_bytes()); + let third = store_cipher.hash_key(table_name, self.2.as_bytes()); + + [ + first.as_slice(), + &[Self::SEPARATOR], + second.as_slice(), + &[Self::SEPARATOR], + third.as_slice(), + &[Self::SEPARATOR], + ] + .concat() + } +} + +impl EncodeSecureKey for Session { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec { + let sender_key = store_cipher.hash_key(table_name, self.sender_key().as_bytes()); + let session_id = store_cipher.hash_key(table_name, self.session_id().as_bytes()); + + [sender_key.as_slice(), &[Self::SEPARATOR], session_id.as_slice(), &[Self::SEPARATOR]] + .concat() + } +} + impl EncodeKey for RoomId { fn encode(&self) -> Vec { self.as_str().encode() @@ -141,13 +276,37 @@ impl EncodeKey for str { } } +impl EncodeKey for ([u8; N], [u8; N]) { + fn encode(&self) -> Vec { + [self.0.as_slice(), &[Self::SEPARATOR], self.1.as_slice(), &[Self::SEPARATOR]].concat() + } +} + impl EncodeKey for (&str, &str) { fn encode(&self) -> Vec { [self.0.as_bytes(), &[Self::SEPARATOR], self.1.as_bytes(), &[Self::SEPARATOR]].concat() } } -impl EncodeKey for (&str, &str, &str) { +impl EncodeKey for (&UserId, &DeviceId) { + fn encode(&self) -> Vec { + (self.0.as_str(), self.1.as_str()).encode() + } +} + +impl EncodeKey for InboundGroupSession { + fn encode(&self) -> Vec { + (self.room_id(), self.sender_key(), self.session_id()).encode() + } +} + +impl EncodeKey for OutboundGroupSession { + fn encode(&self) -> Vec { + self.room_id().encode() + } +} + +impl EncodeKey for (&RoomId, &str, &str) { fn encode(&self) -> Vec { [ self.0.as_bytes(), @@ -161,6 +320,10 @@ impl EncodeKey for (&str, &str, &str) { } } +trait EncodeSecureKey { + fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec; +} + #[derive(Clone, Debug)] pub struct AccountInfo { user_id: Arc, @@ -168,12 +331,19 @@ pub struct AccountInfo { identity_keys: Arc, } +#[derive(Debug, Serialize, Deserialize)] +struct TrackedUser { + user_id: Box, + dirty: bool, +} + /// A [sled] based cryptostore. /// /// [sled]: https://github.com/spacejam/sled#readme #[derive(Clone)] pub struct SledStore { account_info: Arc>>, + store_cipher: Arc>, path: Option, inner: Db, @@ -236,16 +406,44 @@ impl SledStore { self.account_info.read().unwrap().clone() } + fn serialize_value(&self, event: &impl Serialize) -> Result, CryptoStoreError> { + if let Some(key) = &*self.store_cipher { + key.encrypt_value(event).map_err(|e| CryptoStoreError::Backend(anyhow!(e))) + } else { + Ok(serde_json::to_vec(event)?) + } + } + + fn deserialize_value Deserialize<'b>>( + &self, + event: &[u8], + ) -> Result { + if let Some(key) = &*self.store_cipher { + key.decrypt_value(event).map_err(|e| CryptoStoreError::Backend(anyhow!(e))) + } else { + Ok(serde_json::from_slice(event)?) + } + } + + fn encode_key( + &self, + table_name: &str, + key: &T, + ) -> Vec { + if let Some(store_cipher) = &*self.store_cipher { + key.encode_secure(table_name, store_cipher).to_vec() + } else { + key.encode() + } + } + async fn reset_backup_state(&self) -> Result<()> { let mut pickles: Vec<(IVec, PickledInboundGroupSession)> = self .inbound_group_sessions .iter() .map(|p| { let item = p.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; - Ok(( - item.0, - serde_json::from_slice(&item.1).map_err(CryptoStoreError::Serialization)?, - )) + Ok((item.0, self.deserialize_value(&item.1)?)) }) .collect::>()?; @@ -253,12 +451,13 @@ impl SledStore { pickle.backed_up = false; } - let ret: Result<(), TransactionError> = + let ret: Result<(), TransactionError> = self.inbound_group_sessions.transaction(|inbound_sessions| { for (key, pickle) in &pickles { inbound_sessions.insert( key, - serde_json::to_vec(&pickle).map_err(ConflictableTransactionError::Abort)?, + self.serialize_value(pickle) + .map_err(ConflictableTransactionError::Abort)?, )?; } @@ -381,10 +580,29 @@ impl SledStore { Ok(()) } + fn get_or_create_store_cipher(passphrase: &str, database: &Db) -> Result { + let key = if let Some(key) = database + .get("store_cipher".encode()) + .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? + { + StoreCipher::import(passphrase, &key).map_err(|_| CryptoStoreError::UnpicklingError)? + } else { + let key = StoreCipher::new().map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + let encrypted = + key.export(passphrase).map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + database + .insert("store_cipher".encode(), encrypted) + .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + key + }; + + Ok(key) + } + fn open_helper( db: Db, path: Option, - _passphrase: Option<&str>, + passphrase: Option<&str>, ) -> Result { let account = db.open_tree("account")?; let private_identity = db.open_tree("private_identity")?; @@ -406,10 +624,18 @@ impl SledStore { let session_cache = SessionStore::new(); + let store_cipher = if let Some(passphrase) = passphrase { + Some(Self::get_or_create_store_cipher(passphrase, &db)?) + } else { + None + } + .into(); + let database = Self { account_info: RwLock::new(None).into(), path, inner: db, + store_cipher, account, private_identity, sessions, @@ -434,14 +660,13 @@ impl SledStore { async fn load_tracked_users(&self) -> Result<()> { for value in &self.tracked_users { - let (user, dirty) = value.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; - let user = UserId::parse(String::from_utf8_lossy(&user).to_string())?; - let dirty = dirty.get(0).map(|d| *d == 1).unwrap_or(true); + let (_, user) = value.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + let user: TrackedUser = self.deserialize_value(&user)?; - self.tracked_users_cache.insert(user.to_owned()); + self.tracked_users_cache.insert(user.user_id.to_owned()); - if dirty { - self.users_for_key_query_cache.insert(user); + if user.dirty { + self.users_for_key_query_cache.insert(user.user_id); } } @@ -455,9 +680,9 @@ impl SledStore { let account_info = self.get_account_info().ok_or(CryptoStoreError::AccountUnset)?; self.outbound_group_sessions - .get(room_id.encode()) + .get(self.encode_key(OUTBOUND_GROUP_TABLE_NAME, room_id)) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|p| serde_json::from_slice(&p).map_err(CryptoStoreError::Serialization)) + .map(|p| self.deserialize_value(&p)) .transpose()? .map(|p| { Ok(OutboundGroupSession::from_pickle( @@ -482,11 +707,8 @@ impl SledStore { let mut session_changes = HashMap::new(); for session in changes.sessions { - let sender_key = session.sender_key(); - let session_id = session.session_id(); - let pickle = session.pickle().await; - let key = (sender_key, session_id).encode(); + let key = self.encode_key(SESSIONS_TABLE_NAME, &session); self.session_cache.add(session).await; session_changes.insert(key, pickle); @@ -495,10 +717,7 @@ impl SledStore { let mut inbound_session_changes = HashMap::new(); for session in changes.inbound_group_sessions { - let room_id = session.room_id(); - let sender_key = session.sender_key(); - let session_id = session.session_id(); - let key = (room_id.as_str(), sender_key, session_id).encode(); + let key = self.encode_key(INBOUND_GROUP_TABLE_NAME, &session); let pickle = session.pickle().await; inbound_session_changes.insert(key, pickle); @@ -507,10 +726,10 @@ impl SledStore { let mut outbound_session_changes = HashMap::new(); for session in changes.outbound_group_sessions { - let room_id = session.room_id(); + let key = self.encode_key(OUTBOUND_GROUP_TABLE_NAME, &session); let pickle = session.pickle().await; - outbound_session_changes.insert(room_id.to_owned(), pickle); + outbound_session_changes.insert(key, pickle); } let identity_changes = changes.identities; @@ -518,7 +737,7 @@ impl SledStore { let key_requests = changes.key_requests; let backup_version = changes.backup_version; - let ret: Result<(), TransactionError> = ( + let ret: Result<(), TransactionError> = ( &self.account, &self.private_identity, &self.devices, @@ -548,47 +767,49 @@ impl SledStore { if let Some(a) = &account_pickle { account.insert( "account".encode(), - serde_json::to_vec(a).map_err(ConflictableTransactionError::Abort)?, + self.serialize_value(a).map_err(ConflictableTransactionError::Abort)?, )?; } if let Some(i) = &private_identity_pickle { private_identity.insert( "identity".encode(), - serde_json::to_vec(&i).map_err(ConflictableTransactionError::Abort)?, + self.serialize_value(&i) + .map_err(ConflictableTransactionError::Abort)?, )?; } if let Some(r) = &recovery_key_pickle { account.insert( "recovery_key_v1".encode(), - serde_json::to_vec(r).map_err(ConflictableTransactionError::Abort)?, + self.serialize_value(r).map_err(ConflictableTransactionError::Abort)?, )?; } if let Some(b) = &backup_version { account.insert( "backup_version_v1".encode(), - serde_json::to_vec(b).map_err(ConflictableTransactionError::Abort)?, + self.serialize_value(b).map_err(ConflictableTransactionError::Abort)?, )?; } for device in device_changes.new.iter().chain(&device_changes.changed) { - let key = device.encode(); - let device = serde_json::to_vec(&device) + let key = self.encode_key(DEVICE_TABLE_NAME, device); + let device = self + .serialize_value(&device) .map_err(ConflictableTransactionError::Abort)?; devices.insert(key, device)?; } for device in &device_changes.deleted { - let key = device.encode(); + let key = self.encode_key(DEVICE_TABLE_NAME, device); devices.remove(key)?; } for identity in identity_changes.changed.iter().chain(&identity_changes.new) { identities.insert( - identity.user_id().encode(), - serde_json::to_vec(&identity) + self.encode_key(IDENTITIES_TABLE_NAME, identity.user_id()), + self.serialize_value(&identity) .map_err(ConflictableTransactionError::Abort)?, )?; } @@ -596,7 +817,7 @@ impl SledStore { for (key, session) in &session_changes { sessions.insert( key.as_slice(), - serde_json::to_vec(&session) + self.serialize_value(&session) .map_err(ConflictableTransactionError::Abort)?, )?; } @@ -604,30 +825,33 @@ impl SledStore { for (key, session) in &inbound_session_changes { inbound_sessions.insert( key.as_slice(), - serde_json::to_vec(&session) + self.serialize_value(&session) .map_err(ConflictableTransactionError::Abort)?, )?; } for (key, session) in &outbound_session_changes { outbound_sessions.insert( - key.encode(), - serde_json::to_vec(&session) + key.as_slice(), + self.serialize_value(&session) .map_err(ConflictableTransactionError::Abort)?, )?; } for hash in &olm_hashes { hashes.insert( - serde_json::to_vec(&hash) + serde_json::to_vec(hash) + .map_err(CryptoStoreError::Serialization) .map_err(ConflictableTransactionError::Abort)?, &[0], )?; } for key_request in &key_requests { - secret_requests_by_info - .insert(key_request.info.encode(), key_request.request_id.encode())?; + secret_requests_by_info.insert( + self.encode_key(SECRET_REQUEST_BY_INFO_TABLE, &key_request.info), + key_request.request_id.encode(), + )?; let key_request_id = key_request.request_id.encode(); @@ -635,14 +859,14 @@ impl SledStore { unsent_secret_requests.remove(key_request_id.clone())?; outgoing_secret_requests.insert( key_request_id, - serde_json::to_vec(&key_request) + self.serialize_value(&key_request) .map_err(ConflictableTransactionError::Abort)?, )?; } else { outgoing_secret_requests.remove(key_request_id.clone())?; unsent_secret_requests.insert( key_request_id, - serde_json::to_vec(&key_request) + self.serialize_value(&key_request) .map_err(ConflictableTransactionError::Abort)?, )?; } @@ -653,7 +877,7 @@ impl SledStore { ); ret.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; - self.inner.flush_async().await.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; + self.inner.flush().map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; Ok(()) } @@ -663,14 +887,14 @@ impl SledStore { .outgoing_secret_requests .get(id) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|r| serde_json::from_slice(&r)) + .map(|r| self.deserialize_value(&r)) .transpose()?; let request = if request.is_none() { self.unsent_secret_requests .get(id) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|r| serde_json::from_slice(&r)) + .map(|r| self.deserialize_value(&r)) .transpose()? } else { request @@ -688,7 +912,7 @@ impl CryptoStore for SledStore { .get("account".encode()) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? { - let pickle = serde_json::from_slice(&pickle)?; + let pickle = self.deserialize_value(&pickle)?; self.load_tracked_users().await?; let account = ReadOnlyAccount::from_pickle(pickle)?; @@ -727,7 +951,7 @@ impl CryptoStore for SledStore { .get("identity".encode()) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? { - let pickle = serde_json::from_slice(&i)?; + let pickle = self.deserialize_value(&i)?; Ok(Some( PrivateCrossSigningIdentity::from_pickle(pickle) .await @@ -748,10 +972,9 @@ impl CryptoStore for SledStore { if self.session_cache.get(sender_key).is_none() { let sessions: Result> = self .sessions - .scan_prefix(sender_key.encode()) + .scan_prefix(self.encode_key(SESSIONS_TABLE_NAME, sender_key)) .map(|s| { - serde_json::from_slice(&s.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) - .map_err(CryptoStoreError::Serialization) + self.deserialize_value(&s.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) }) .map(|p| { Ok(Session::from_pickle( @@ -775,12 +998,12 @@ impl CryptoStore for SledStore { sender_key: &str, session_id: &str, ) -> Result> { - let key = (room_id.as_str(), sender_key, session_id).encode(); + let key = self.encode_key(INBOUND_GROUP_TABLE_NAME, &(room_id, sender_key, session_id)); let pickle = self .inbound_group_sessions .get(&key) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|p| serde_json::from_slice(&p)); + .map(|p| self.deserialize_value(&p)); if let Some(pickle) = pickle { Ok(Some(InboundGroupSession::from_pickle(pickle?)?)) @@ -794,8 +1017,7 @@ impl CryptoStore for SledStore { .inbound_group_sessions .iter() .map(|p| { - serde_json::from_slice(&p.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) - .map_err(CryptoStoreError::Serialization) + self.deserialize_value(&p.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) }) .collect(); @@ -808,7 +1030,7 @@ impl CryptoStore for SledStore { .iter() .map(|p| { let item = p.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; - serde_json::from_slice(&item.1).map_err(CryptoStoreError::Serialization) + self.deserialize_value(&item.1) }) .collect::>()?; @@ -827,7 +1049,7 @@ impl CryptoStore for SledStore { .iter() .map(|p| { let item = p.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; - serde_json::from_slice(&item.1).map_err(CryptoStoreError::from) + self.deserialize_value(&item.1) }) .filter_map(|p: Result| match p { Ok(p) => { @@ -882,8 +1104,13 @@ impl CryptoStore for SledStore { self.users_for_key_query_cache.remove(user); } + let user = TrackedUser { user_id: user.to_owned(), dirty }; + self.tracked_users - .insert(user.as_str(), &[dirty as u8]) + .insert( + self.encode_key(TRACKED_USERS_TABLE, user.user_id.as_str()), + self.serialize_value(&user)?, + ) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; Ok(already_added) @@ -894,12 +1121,13 @@ impl CryptoStore for SledStore { user_id: &UserId, device_id: &DeviceId, ) -> Result> { - let key = (user_id.as_str(), device_id.as_str()).encode(); + let key = self.encode_key(DEVICE_TABLE_NAME, &(user_id, device_id)); + Ok(self .devices .get(key) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|d| serde_json::from_slice(&d)) + .map(|d| self.deserialize_value(&d)) .transpose()?) } @@ -907,11 +1135,11 @@ impl CryptoStore for SledStore { &self, user_id: &UserId, ) -> Result, ReadOnlyDevice>> { + let key = self.encode_key(DEVICE_TABLE_NAME, user_id); self.devices - .scan_prefix(user_id.encode()) + .scan_prefix(key) .map(|d| { - serde_json::from_slice(&d.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) - .map_err(CryptoStoreError::Serialization) + self.deserialize_value(&d.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) }) .map(|d| { let d: ReadOnlyDevice = d?; @@ -921,11 +1149,13 @@ impl CryptoStore for SledStore { } async fn get_user_identity(&self, user_id: &UserId) -> Result> { + let key = self.encode_key(IDENTITIES_TABLE_NAME, user_id); + Ok(self .identities - .get(user_id.encode()) + .get(key) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|i| serde_json::from_slice(&i)) + .map(|i| self.deserialize_value(&i)) .transpose()?) } @@ -954,7 +1184,7 @@ impl CryptoStore for SledStore { ) -> Result> { let id = self .secret_requests_by_info - .get(key_info.encode()) + .get(self.encode_key(SECRET_REQUEST_BY_INFO_TABLE, key_info)) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?; if let Some(id) = id { @@ -969,8 +1199,7 @@ impl CryptoStore for SledStore { .unsent_secret_requests .iter() .map(|i| { - serde_json::from_slice(&i.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) - .map_err(CryptoStoreError::from) + self.deserialize_value(&i.map_err(|e| CryptoStoreError::Backend(anyhow!(e)))?.1) }) .collect(); @@ -978,7 +1207,7 @@ impl CryptoStore for SledStore { } async fn delete_outgoing_secret_requests(&self, request_id: &TransactionId) -> Result<()> { - let ret: Result<(), TransactionError> = ( + let ret: Result<(), TransactionError> = ( &self.outgoing_secret_requests, &self.unsent_secret_requests, &self.secret_requests_by_info, @@ -987,22 +1216,24 @@ impl CryptoStore for SledStore { |(outgoing_key_requests, unsent_key_requests, key_requests_by_info)| { let sent_request: Option = outgoing_key_requests .remove(request_id.encode())? - .map(|r| serde_json::from_slice(&r)) + .map(|r| self.deserialize_value(&r)) .transpose() .map_err(ConflictableTransactionError::Abort)?; let unsent_request: Option = unsent_key_requests .remove(request_id.encode())? - .map(|r| serde_json::from_slice(&r)) + .map(|r| self.deserialize_value(&r)) .transpose() .map_err(ConflictableTransactionError::Abort)?; if let Some(request) = sent_request { - key_requests_by_info.remove(request.info.encode())?; + key_requests_by_info + .remove(self.encode_key(SECRET_REQUEST_BY_INFO_TABLE, &request.info))?; } if let Some(request) = unsent_request { - key_requests_by_info.remove(request.info.encode())?; + key_requests_by_info + .remove(self.encode_key(SECRET_REQUEST_BY_INFO_TABLE, &request.info))?; } Ok(()) @@ -1021,14 +1252,14 @@ impl CryptoStore for SledStore { .account .get("backup_version_v1".encode()) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|v| serde_json::from_slice(&v)) + .map(|v| self.deserialize_value(&v)) .transpose()?; let recovery_key = { self.account .get("recovery_key_v1".encode()) .map_err(|e| CryptoStoreError::Backend(anyhow!(e)))? - .map(|p| serde_json::from_slice(&p)) + .map(|p| self.deserialize_value(&p)) .transpose()? };