crypto: Add a method to save/load arbitrary objects from a CryptoStore.

This actually adds trait methods that save/load strings from the
CryptoStore. We add a wrapper for the CryptoStore since we can't mix
trait objects and generics, so we add generic methods to save/load
anything that implements Serialize/Deserialize.
master
Damir Jelić 2020-09-16 16:03:19 +02:00
parent 849934b180
commit 24ce4881c7
7 changed files with 87 additions and 24 deletions

View File

@ -651,7 +651,7 @@ impl OwnUserIdentity {
#[cfg(test)]
pub(crate) mod test {
use serde_json::json;
use std::{convert::TryFrom, sync::Arc};
use std::convert::TryFrom;
use matrix_sdk_common::{
api::r0::keys::get_keys::Response as KeyQueryResponse, identifiers::user_id,
@ -661,7 +661,7 @@ pub(crate) mod test {
identities::{Device, ReadOnlyDevice},
machine::test::response_from_file,
olm::Account,
store::MemoryStore,
store::{MemoryStore, Store},
verification::VerificationMachine,
};
@ -884,7 +884,7 @@ pub(crate) mod test {
let verification_machine = VerificationMachine::new(
Account::new(second.user_id(), second.device_id()),
Arc::new(Box::new(MemoryStore::new())),
Store::new(Box::new(MemoryStore::new())),
);
let first = Device {

View File

@ -62,7 +62,7 @@ use super::{
InboundGroupSession, OlmMessage, OutboundGroupSession,
},
requests::{IncomingResponse, KeysQueryRequest, OutgoingRequest, ToDeviceRequest},
store::{CryptoStore, MemoryStore, Result as StoreResult},
store::{CryptoStore, MemoryStore, Result as StoreResult, Store},
verification::{Sas, VerificationMachine},
};
@ -79,7 +79,7 @@ pub struct OlmMachine {
/// Store for the encryption keys.
/// Persists all the encryption keys so a client can resume the session
/// without the need to create new keys.
store: Arc<Box<dyn CryptoStore>>,
store: Store,
/// The currently active outbound group sessions.
outbound_group_sessions: Arc<DashMap<RoomId, OutboundGroupSession>>,
/// A state machine that is responsible to handle and keep track of SAS
@ -113,7 +113,7 @@ impl OlmMachine {
/// * `device_id` - The unique id of the device that owns this machine.
pub fn new(user_id: &UserId, device_id: &DeviceId) -> Self {
let store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
let store = Arc::new(store);
let store = Store::new(store);
let account = Account::new(user_id, device_id);
OlmMachine {
@ -161,7 +161,7 @@ impl OlmMachine {
}
};
let store = Arc::new(store);
let store = Store::new(store);
let verification_machine = VerificationMachine::new(account.clone(), store.clone());
Ok(OlmMachine {

View File

@ -36,6 +36,7 @@ pub struct MemoryStore {
users_for_key_query: Arc<DashSet<UserId>>,
devices: DeviceStore,
identities: Arc<DashMap<UserId, UserIdentities>>,
values: Arc<DashMap<String, String>>,
}
impl Default for MemoryStore {
@ -47,6 +48,7 @@ impl Default for MemoryStore {
users_for_key_query: Arc::new(DashSet::new()),
devices: DeviceStore::new(),
identities: Arc::new(DashMap::new()),
values: Arc::new(DashMap::new()),
}
}
}
@ -164,6 +166,15 @@ impl CryptoStore for MemoryStore {
}
Ok(())
}
async fn save_value(&self, key: String, value: String) -> Result<()> {
self.values.insert(key, value);
Ok(())
}
async fn get_value(&self, key: &str) -> Result<Option<String>> {
Ok(self.values.get(key).map(|v| v.to_owned()))
}
}
#[cfg(test)]

View File

@ -49,9 +49,10 @@ pub use memorystore::MemoryStore;
#[cfg(feature = "sqlite_cryptostore")]
pub use sqlite::SqliteStore;
use std::{collections::HashSet, fmt::Debug, io::Error as IoError, sync::Arc};
use std::{collections::HashSet, fmt::Debug, io::Error as IoError, ops::Deref, sync::Arc};
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
use serde::{Deserialize, Serialize};
use serde_json::Error as SerdeError;
use thiserror::Error;
use url::ParseError;
@ -76,6 +77,44 @@ use super::{
use crate::error::SessionUnpicklingError;
/// A wrapper for our CryptoStore trait object.
///
/// This is needed because we want to have a generic interface so we can
/// store/restore objects that we can serialize. Since trait objects and
/// generics don't mix let the CryptoStore store strings and this wrapper
/// adds the generic interface on top.
#[derive(Debug, Clone)]
pub(crate) struct Store(Arc<Box<dyn CryptoStore>>);
impl Store {
pub fn new(store: Box<dyn CryptoStore>) -> Self {
Self(Arc::new(store))
}
#[allow(dead_code)]
pub async fn get_object<V: for<'b> Deserialize<'b>>(&self, key: &str) -> Result<Option<V>> {
if let Some(value) = self.get_value(key).await? {
Ok(Some(serde_json::from_str(&value)?))
} else {
Ok(None)
}
}
#[allow(dead_code)]
pub async fn save_object(&self, key: &str, value: &impl Serialize) -> Result<()> {
let value = serde_json::to_string(value)?;
self.save_value(key.to_owned(), value).await
}
}
impl Deref for Store {
type Target = dyn CryptoStore;
fn deref(&self) -> &Self::Target {
&**self.0
}
}
/// A `CryptoStore` specific result type.
pub type Result<T> = std::result::Result<T, CryptoStoreError>;
@ -250,4 +289,10 @@ pub trait CryptoStore: Debug {
///
/// * `user_id` - The user for which we should get the identity.
async fn get_user_identity(&self, user_id: &UserId) -> Result<Option<UserIdentities>>;
/// Save a serializeable object in the store.
async fn save_value(&self, key: String, value: String) -> Result<()>;
/// Load a serializeable object from the store.
async fn get_value(&self, key: &str) -> Result<Option<String>>;
}

View File

@ -1371,6 +1371,14 @@ impl CryptoStore for SqliteStore {
Ok(())
}
async fn save_value(&self, _key: String, _value: String) -> Result<()> {
todo!()
}
async fn get_value(&self, _key: &str) -> Result<Option<String>> {
todo!()
}
}
#[cfg(not(tarpaulin_include))]

View File

@ -27,20 +27,20 @@ use matrix_sdk_common::{
use super::sas::{content_to_request, Sas};
use crate::{
requests::{OutgoingRequest, ToDeviceRequest},
store::{CryptoStore, CryptoStoreError},
store::{CryptoStoreError, Store},
Account, ReadOnlyDevice,
};
#[derive(Clone, Debug)]
pub struct VerificationMachine {
account: Account,
pub(crate) store: Arc<Box<dyn CryptoStore>>,
pub(crate) store: Store,
verifications: Arc<DashMap<String, Sas>>,
outgoing_to_device_messages: Arc<DashMap<Uuid, OutgoingRequest>>,
}
impl VerificationMachine {
pub(crate) fn new(account: Account, store: Arc<Box<dyn CryptoStore>>) -> Self {
pub(crate) fn new(account: Account, store: Store) -> Self {
Self {
account,
store,
@ -221,7 +221,6 @@ mod test {
use std::{
convert::TryFrom,
sync::Arc,
time::{Duration, Instant},
};
@ -233,7 +232,7 @@ mod test {
use super::{Sas, VerificationMachine};
use crate::{
requests::OutgoingRequests,
store::{CryptoStore, MemoryStore},
store::{CryptoStore, MemoryStore, Store},
verification::test::{get_content_from_request, wrap_any_to_device_content},
Account, ReadOnlyDevice,
};
@ -258,7 +257,7 @@ mod test {
let alice = Account::new(&alice_id(), &alice_device_id());
let bob = Account::new(&bob_id(), &bob_device_id());
let store = MemoryStore::new();
let bob_store: Arc<Box<dyn CryptoStore>> = Arc::new(Box::new(MemoryStore::new()));
let bob_store = Store::new(Box::new(MemoryStore::new()));
let bob_device = ReadOnlyDevice::from_account(&bob).await;
let alice_device = ReadOnlyDevice::from_account(&alice).await;
@ -269,7 +268,7 @@ mod test {
.await
.unwrap();
let machine = VerificationMachine::new(alice, Arc::new(Box::new(store)));
let machine = VerificationMachine::new(alice, Store::new(Box::new(store)));
let (bob_sas, start_content) = Sas::start(bob, alice_device, bob_store, None);
machine
.receive_event(&mut wrap_any_to_device_content(
@ -286,7 +285,7 @@ mod test {
fn create() {
let alice = Account::new(&alice_id(), &alice_device_id());
let store = MemoryStore::new();
let _ = VerificationMachine::new(alice, Arc::new(Box::new(store)));
let _ = VerificationMachine::new(alice, Store::new(Box::new(store)));
}
#[tokio::test]

View File

@ -34,7 +34,7 @@ use matrix_sdk_common::{
use crate::{
identities::{LocalTrust, ReadOnlyDevice, UserIdentities},
store::{CryptoStore, CryptoStoreError},
store::{CryptoStoreError, Store},
Account, ToDeviceRequest,
};
@ -47,7 +47,7 @@ use sas_state::{
/// Short authentication string object.
pub struct Sas {
inner: Arc<Mutex<InnerSas>>,
store: Arc<Box<dyn CryptoStore>>,
store: Store,
account: Account,
other_device: ReadOnlyDevice,
other_identity: Option<UserIdentities>,
@ -104,7 +104,7 @@ impl Sas {
pub(crate) fn start(
account: Account,
other_device: ReadOnlyDevice,
store: Arc<Box<dyn CryptoStore>>,
store: Store,
other_identity: Option<UserIdentities>,
) -> (Sas, StartEventContent) {
let (inner, content) = InnerSas::start(
@ -139,7 +139,7 @@ impl Sas {
pub(crate) fn from_start_event(
account: Account,
other_device: ReadOnlyDevice,
store: Arc<Box<dyn CryptoStore>>,
store: Store,
event: &ToDeviceEvent<StartEventContent>,
other_identity: Option<UserIdentities>,
) -> Result<Sas, AnyToDeviceEventContent> {
@ -646,7 +646,7 @@ impl InnerSas {
#[cfg(test)]
mod test {
use std::{convert::TryFrom, sync::Arc};
use std::convert::TryFrom;
use matrix_sdk_common::{
events::{EventContent, ToDeviceEvent},
@ -654,7 +654,7 @@ mod test {
};
use crate::{
store::{CryptoStore, MemoryStore},
store::{MemoryStore, Store},
verification::test::{get_content_from_request, wrap_any_to_device_content},
Account, ReadOnlyDevice,
};
@ -776,8 +776,8 @@ mod test {
let bob = Account::new(&bob_id(), &bob_device_id());
let bob_device = ReadOnlyDevice::from_account(&bob).await;
let alice_store: Arc<Box<dyn CryptoStore>> = Arc::new(Box::new(MemoryStore::new()));
let bob_store: Arc<Box<dyn CryptoStore>> = Arc::new(Box::new(MemoryStore::new()));
let alice_store = Store::new(Box::new(MemoryStore::new()));
let bob_store = Store::new(Box::new(MemoryStore::new()));
bob_store
.save_devices(&[alice_device.clone()])