mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-25 01:02:20 -04:00
reuse cryptostore for sled
This commit is contained in:
@@ -74,6 +74,7 @@ tokio = { version = "1.7.1", default-features = false, features = [
|
||||
"rt-multi-thread",
|
||||
"macros",
|
||||
] }
|
||||
lazy_static = "1.4"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dev-dependencies]
|
||||
criterion = { version = "0.3.4", features = ["async", "html_reports"] }
|
||||
|
||||
@@ -937,509 +937,27 @@ impl CryptoStore for SledStore {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use matrix_sdk_common::uuid::Uuid;
|
||||
use matrix_sdk_test::async_test;
|
||||
use olm_rs::outbound_group_session::OlmOutboundGroupSession;
|
||||
use ruma::{
|
||||
encryption::SignedKey, events::room_key_request::RequestedKeyInfo, room_id, user_id,
|
||||
DeviceId, EventEncryptionAlgorithm, UserId,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::{CryptoStore, GossipRequest, SledStore};
|
||||
use crate::{
|
||||
gossiping::SecretInfo,
|
||||
identities::{
|
||||
device::test::get_device,
|
||||
user::test::{get_other_identity, get_own_identity},
|
||||
},
|
||||
olm::{
|
||||
GroupSessionKey, InboundGroupSession, OlmMessageHash, PrivateCrossSigningIdentity,
|
||||
ReadOnlyAccount, Session,
|
||||
},
|
||||
store::{Changes, DeviceChanges, IdentityChanges},
|
||||
};
|
||||
|
||||
fn alice_id() -> UserId {
|
||||
user_id!("@alice:example.org")
|
||||
#[macro_use]
|
||||
use super::super::integration_tests;
|
||||
use super::SledStore;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
use lazy_static::lazy_static;
|
||||
lazy_static! {
|
||||
/// This is an example for using doc comment attributes
|
||||
static ref TMP_DIR: TempDir = tempdir().unwrap();
|
||||
}
|
||||
|
||||
fn alice_device_id() -> Box<DeviceId> {
|
||||
"ALICEDEVICE".into()
|
||||
}
|
||||
async fn get_store(name: String, passphrase: Option<&str>) -> SledStore {
|
||||
let tmpdir_path = TMP_DIR.path().clone().join(name);
|
||||
|
||||
fn bob_id() -> UserId {
|
||||
user_id!("@bob:example.org")
|
||||
}
|
||||
|
||||
fn bob_device_id() -> Box<DeviceId> {
|
||||
"BOBDEVICE".into()
|
||||
}
|
||||
|
||||
async fn get_store(passphrase: Option<&str>) -> (SledStore, tempfile::TempDir) {
|
||||
let tmpdir = tempdir().unwrap();
|
||||
let tmpdir_path = tmpdir.path().to_str().unwrap();
|
||||
|
||||
let store = SledStore::open_with_passphrase(tmpdir_path, passphrase)
|
||||
let store = SledStore::open_with_passphrase(tmpdir_path.to_str().unwrap(), passphrase)
|
||||
.expect("Can't create a passphrase protected store");
|
||||
|
||||
(store, tmpdir)
|
||||
store
|
||||
}
|
||||
|
||||
async fn get_loaded_store() -> (ReadOnlyAccount, SledStore, tempfile::TempDir) {
|
||||
let (store, dir) = get_store(None).await;
|
||||
let account = get_account();
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
(account, store, dir)
|
||||
}
|
||||
|
||||
fn get_account() -> ReadOnlyAccount {
|
||||
ReadOnlyAccount::new(&alice_id(), &alice_device_id())
|
||||
}
|
||||
|
||||
async fn get_account_and_session() -> (ReadOnlyAccount, Session) {
|
||||
let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id());
|
||||
let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id());
|
||||
|
||||
bob.generate_one_time_keys_helper(1).await;
|
||||
let one_time_key =
|
||||
bob.one_time_keys().await.curve25519().iter().next().unwrap().1.to_owned();
|
||||
let one_time_key = SignedKey::new(one_time_key, BTreeMap::new());
|
||||
let sender_key = bob.identity_keys().curve25519().to_owned();
|
||||
let session =
|
||||
alice.create_outbound_session_helper(&sender_key, &one_time_key).await.unwrap();
|
||||
|
||||
(alice, session)
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn create_store() {
|
||||
let tmpdir = tempdir().unwrap();
|
||||
let tmpdir_path = tmpdir.path().to_str().unwrap();
|
||||
let _ = SledStore::open_with_passphrase(tmpdir_path, None).expect("Can't create store");
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn save_account() {
|
||||
let (store, _dir) = get_store(None).await;
|
||||
assert!(store.load_account().await.unwrap().is_none());
|
||||
let account = get_account();
|
||||
|
||||
store.save_account(account).await.expect("Can't save account");
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn load_account() {
|
||||
let (store, _dir) = get_store(None).await;
|
||||
let account = get_account();
|
||||
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
let loaded_account = store.load_account().await.expect("Can't load account");
|
||||
let loaded_account = loaded_account.unwrap();
|
||||
|
||||
assert_eq!(account, loaded_account);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn load_account_with_passphrase() {
|
||||
let (store, _dir) = get_store(Some("secret_passphrase")).await;
|
||||
let account = get_account();
|
||||
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
let loaded_account = store.load_account().await.expect("Can't load account");
|
||||
let loaded_account = loaded_account.unwrap();
|
||||
|
||||
assert_eq!(account, loaded_account);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn save_and_share_account() {
|
||||
let (store, _dir) = get_store(None).await;
|
||||
let account = get_account();
|
||||
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
account.mark_as_shared();
|
||||
account.update_uploaded_key_count(50);
|
||||
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
let loaded_account = store.load_account().await.expect("Can't load account");
|
||||
let loaded_account = loaded_account.unwrap();
|
||||
|
||||
assert_eq!(account, loaded_account);
|
||||
assert_eq!(account.uploaded_key_count(), loaded_account.uploaded_key_count());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn load_sessions() {
|
||||
let (store, _dir) = get_store(None).await;
|
||||
let (account, session) = get_account_and_session().await;
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
let changes = Changes { sessions: vec![session.clone()], ..Default::default() };
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
let sessions =
|
||||
store.get_sessions(&session.sender_key).await.expect("Can't load sessions").unwrap();
|
||||
let loaded_session = sessions.lock().await.get(0).cloned().unwrap();
|
||||
|
||||
assert_eq!(&session, &loaded_session);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn add_and_save_session() {
|
||||
let (store, dir) = get_store(None).await;
|
||||
let (account, session) = get_account_and_session().await;
|
||||
let sender_key = session.sender_key.to_owned();
|
||||
let session_id = session.session_id().to_owned();
|
||||
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
let changes = Changes { sessions: vec![session.clone()], ..Default::default() };
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
let sessions = store.get_sessions(&sender_key).await.unwrap().unwrap();
|
||||
let sessions_lock = sessions.lock().await;
|
||||
let session = &sessions_lock[0];
|
||||
|
||||
assert_eq!(session_id, session.session_id());
|
||||
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
let loaded_account = store.load_account().await.unwrap().unwrap();
|
||||
assert_eq!(account, loaded_account);
|
||||
|
||||
let sessions = store.get_sessions(&sender_key).await.unwrap().unwrap();
|
||||
let sessions_lock = sessions.lock().await;
|
||||
let session = &sessions_lock[0];
|
||||
|
||||
assert_eq!(session_id, session.session_id());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn save_inbound_group_session() {
|
||||
let (account, store, _dir) = get_loaded_store().await;
|
||||
|
||||
let identity_keys = account.identity_keys();
|
||||
let outbound_session = OlmOutboundGroupSession::new();
|
||||
let session = InboundGroupSession::new(
|
||||
identity_keys.curve25519(),
|
||||
identity_keys.ed25519(),
|
||||
&room_id!("!test:localhost"),
|
||||
GroupSessionKey(outbound_session.session_key()),
|
||||
None,
|
||||
)
|
||||
.expect("Can't create session");
|
||||
|
||||
let changes = Changes { inbound_group_sessions: vec![session], ..Default::default() };
|
||||
|
||||
store.save_changes(changes).await.expect("Can't save group session");
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn load_inbound_group_session() {
|
||||
let (account, store, dir) = get_loaded_store().await;
|
||||
|
||||
let identity_keys = account.identity_keys();
|
||||
let outbound_session = OlmOutboundGroupSession::new();
|
||||
let session = InboundGroupSession::new(
|
||||
identity_keys.curve25519(),
|
||||
identity_keys.ed25519(),
|
||||
&room_id!("!test:localhost"),
|
||||
GroupSessionKey(outbound_session.session_key()),
|
||||
None,
|
||||
)
|
||||
.expect("Can't create session");
|
||||
|
||||
let mut export = session.export().await;
|
||||
|
||||
export.forwarding_curve25519_key_chain = vec!["some_chain".to_owned()];
|
||||
|
||||
let session = InboundGroupSession::from_export(export).unwrap();
|
||||
|
||||
let changes =
|
||||
Changes { inbound_group_sessions: vec![session.clone()], ..Default::default() };
|
||||
|
||||
store.save_changes(changes).await.expect("Can't save group session");
|
||||
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
store.load_account().await.unwrap();
|
||||
|
||||
let loaded_session = store
|
||||
.get_inbound_group_session(&session.room_id, &session.sender_key, session.session_id())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(session, loaded_session);
|
||||
let export = loaded_session.export().await;
|
||||
assert!(!export.forwarding_curve25519_key_chain.is_empty())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_tracked_users() {
|
||||
let (_account, store, dir) = get_loaded_store().await;
|
||||
let device = get_device();
|
||||
|
||||
assert!(store.update_tracked_user(device.user_id(), false).await.unwrap());
|
||||
assert!(!store.update_tracked_user(device.user_id(), false).await.unwrap());
|
||||
|
||||
assert!(store.is_user_tracked(device.user_id()));
|
||||
assert!(!store.users_for_key_query().contains(device.user_id()));
|
||||
assert!(!store.update_tracked_user(device.user_id(), true).await.unwrap());
|
||||
assert!(store.users_for_key_query().contains(device.user_id()));
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
store.load_account().await.unwrap();
|
||||
|
||||
assert!(store.is_user_tracked(device.user_id()));
|
||||
assert!(store.users_for_key_query().contains(device.user_id()));
|
||||
|
||||
store.update_tracked_user(device.user_id(), false).await.unwrap();
|
||||
assert!(!store.users_for_key_query().contains(device.user_id()));
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
store.load_account().await.unwrap();
|
||||
|
||||
assert!(!store.users_for_key_query().contains(device.user_id()));
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn device_saving() {
|
||||
let (_account, store, dir) = get_loaded_store().await;
|
||||
let device = get_device();
|
||||
|
||||
let changes = Changes {
|
||||
devices: DeviceChanges { changed: vec![device.clone()], ..Default::default() },
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
store.load_account().await.unwrap();
|
||||
|
||||
let loaded_device =
|
||||
store.get_device(device.user_id(), device.device_id()).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(device, loaded_device);
|
||||
|
||||
for algorithm in loaded_device.algorithms() {
|
||||
assert!(device.algorithms().contains(algorithm));
|
||||
}
|
||||
assert_eq!(device.algorithms().len(), loaded_device.algorithms().len());
|
||||
assert_eq!(device.keys(), loaded_device.keys());
|
||||
|
||||
let user_devices = store.get_user_devices(device.user_id()).await.unwrap();
|
||||
assert_eq!(&**user_devices.keys().next().unwrap(), device.device_id());
|
||||
assert_eq!(user_devices.values().next().unwrap(), &device);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn device_deleting() {
|
||||
let (_account, store, dir) = get_loaded_store().await;
|
||||
let device = get_device();
|
||||
|
||||
let changes = Changes {
|
||||
devices: DeviceChanges { changed: vec![device.clone()], ..Default::default() },
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
let changes = Changes {
|
||||
devices: DeviceChanges { deleted: vec![device.clone()], ..Default::default() },
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
store.load_account().await.unwrap();
|
||||
|
||||
let loaded_device = store.get_device(device.user_id(), device.device_id()).await.unwrap();
|
||||
|
||||
assert!(loaded_device.is_none());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn user_saving() {
|
||||
let dir = tempdir().unwrap();
|
||||
let tmpdir_path = dir.path().to_str().unwrap();
|
||||
|
||||
let user_id = user_id!("@example:localhost");
|
||||
let device_id: &DeviceId = "WSKKLTJZCL".into();
|
||||
|
||||
let store = SledStore::open_with_passphrase(tmpdir_path, None).expect("Can't create store");
|
||||
|
||||
let account = ReadOnlyAccount::new(&user_id, device_id);
|
||||
|
||||
store.save_account(account.clone()).await.expect("Can't save account");
|
||||
|
||||
let own_identity = get_own_identity();
|
||||
|
||||
let changes = Changes {
|
||||
identities: IdentityChanges {
|
||||
changed: vec![own_identity.clone().into()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
store.save_changes(changes).await.expect("Can't save identity");
|
||||
|
||||
drop(store);
|
||||
|
||||
let store = SledStore::open_with_passphrase(dir.path(), None).expect("Can't create store");
|
||||
|
||||
store.load_account().await.unwrap();
|
||||
|
||||
let loaded_user = store.get_user_identity(own_identity.user_id()).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(loaded_user.master_key(), own_identity.master_key());
|
||||
assert_eq!(loaded_user.self_signing_key(), own_identity.self_signing_key());
|
||||
assert_eq!(loaded_user, own_identity.clone().into());
|
||||
|
||||
let other_identity = get_other_identity();
|
||||
|
||||
let changes = Changes {
|
||||
identities: IdentityChanges {
|
||||
changed: vec![other_identity.clone().into()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
let loaded_user = store.get_user_identity(other_identity.user_id()).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(loaded_user.master_key(), other_identity.master_key());
|
||||
assert_eq!(loaded_user.self_signing_key(), other_identity.self_signing_key());
|
||||
assert_eq!(loaded_user, other_identity.into());
|
||||
|
||||
own_identity.mark_as_verified();
|
||||
|
||||
let changes = Changes {
|
||||
identities: IdentityChanges {
|
||||
changed: vec![own_identity.into()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
let loaded_user = store.get_user_identity(&user_id).await.unwrap().unwrap();
|
||||
assert!(loaded_user.own().unwrap().is_verified())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn private_identity_saving() {
|
||||
let (_, store, _dir) = get_loaded_store().await;
|
||||
assert!(store.load_identity().await.unwrap().is_none());
|
||||
let identity = PrivateCrossSigningIdentity::new(alice_id()).await;
|
||||
|
||||
let changes = Changes { private_identity: Some(identity.clone()), ..Default::default() };
|
||||
|
||||
store.save_changes(changes).await.unwrap();
|
||||
let loaded_identity = store.load_identity().await.unwrap().unwrap();
|
||||
assert_eq!(identity.user_id(), loaded_identity.user_id());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn olm_hash_saving() {
|
||||
let (_, store, _dir) = get_loaded_store().await;
|
||||
|
||||
let hash =
|
||||
OlmMessageHash { sender_key: "test_sender".to_owned(), hash: "test_hash".to_owned() };
|
||||
|
||||
let mut changes = Changes::default();
|
||||
changes.message_hashes.push(hash.clone());
|
||||
|
||||
assert!(!store.is_message_known(&hash).await.unwrap());
|
||||
store.save_changes(changes).await.unwrap();
|
||||
assert!(store.is_message_known(&hash).await.unwrap());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn key_request_saving() {
|
||||
let (account, store, _dir) = get_loaded_store().await;
|
||||
|
||||
let id = Uuid::new_v4();
|
||||
let info: SecretInfo = RequestedKeyInfo::new(
|
||||
EventEncryptionAlgorithm::MegolmV1AesSha2,
|
||||
room_id!("!test:localhost"),
|
||||
"test_sender_key".to_string(),
|
||||
"test_session_id".to_string(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let request = GossipRequest {
|
||||
request_recipient: account.user_id().to_owned(),
|
||||
request_id: id,
|
||||
info: info.clone(),
|
||||
sent_out: false,
|
||||
};
|
||||
|
||||
assert!(store.get_outgoing_secret_requests(id).await.unwrap().is_none());
|
||||
|
||||
let mut changes = Changes::default();
|
||||
changes.key_requests.push(request.clone());
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
let request = Some(request);
|
||||
|
||||
let stored_request = store.get_outgoing_secret_requests(id).await.unwrap();
|
||||
assert_eq!(request, stored_request);
|
||||
|
||||
let stored_request = store.get_secret_request_by_info(&info).await.unwrap();
|
||||
assert_eq!(request, stored_request);
|
||||
assert!(!store.get_unsent_secret_requests().await.unwrap().is_empty());
|
||||
|
||||
let request = GossipRequest {
|
||||
request_recipient: account.user_id().to_owned(),
|
||||
request_id: id,
|
||||
info: info.clone(),
|
||||
sent_out: true,
|
||||
};
|
||||
|
||||
let mut changes = Changes::default();
|
||||
changes.key_requests.push(request.clone());
|
||||
store.save_changes(changes).await.unwrap();
|
||||
|
||||
assert!(store.get_unsent_secret_requests().await.unwrap().is_empty());
|
||||
let stored_request = store.get_outgoing_secret_requests(id).await.unwrap();
|
||||
assert_eq!(Some(request), stored_request);
|
||||
|
||||
store.delete_outgoing_secret_requests(id).await.unwrap();
|
||||
|
||||
let stored_request = store.get_outgoing_secret_requests(id).await.unwrap();
|
||||
assert_eq!(None, stored_request);
|
||||
|
||||
let stored_request = store.get_secret_request_by_info(&info).await.unwrap();
|
||||
assert_eq!(None, stored_request);
|
||||
assert!(store.get_unsent_secret_requests().await.unwrap().is_empty());
|
||||
}
|
||||
}
|
||||
cryptostore_integration_tests! { integration }
|
||||
}
|
||||
Reference in New Issue
Block a user