Skip to content

crypto: MemoryStore uses backup versions to track which sessions are backed up #3320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/matrix-sdk-crypto/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ Breaking changes:

Additions:

- Expose new method `BackupMachine::backup_version`.
([#3320](https://github.com/matrix-org/matrix-rust-sdk/pull/3320))

- Add data types to parse the QR code data for the QR code login defined in
[MSC4108](https://github.com/matrix-org/matrix-spec-proposals/pull/4108)

Expand Down
7 changes: 7 additions & 0 deletions crates/matrix-sdk-crypto/src/backups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,13 @@ impl BackupMachine {
Ok(())
}

/// Provide the `backup_version` of the current `backup_key`, or None if
/// there is no current key, or the key is not used with any backup
/// version.
pub async fn backup_version(&self) -> Option<String> {
self.backup_key.read().await.as_ref().and_then(|k| k.backup_version())
}

/// Store the backup decryption key in the crypto store.
///
/// This is useful if the client wants to support gossiping of the backup
Expand Down
52 changes: 28 additions & 24 deletions crates/matrix-sdk-crypto/src/store/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,38 +375,42 @@ macro_rules! cryptostore_integration_tests {

#[async_test]
async fn reset_inbound_group_session_for_backup() {
// Given a store exists where all sessions are backed up to backup_1
let (account, store) =
get_loaded_store("reset_inbound_group_session_for_backup").await;
assert_eq!(store.inbound_group_session_counts(None).await.unwrap().total, 0);

let room_id = &room_id!("!test:localhost");
let (_, session) = account.create_group_session_pair_with_defaults(room_id).await;

let changes =
Changes { inbound_group_sessions: vec![session.clone()], ..Default::default() };

let mut sessions: Vec<InboundGroupSession> = Vec::with_capacity(10);
for _ in 0..10 {
sessions.push(account.create_group_session_pair_with_defaults(room_id).await.1);
}
let changes = Changes { inbound_group_sessions: sessions.clone(), ..Default::default() };
store.save_changes(changes).await.expect("Can't save group session");
assert_eq!(store.inbound_group_sessions_for_backup("backup_1", 100).await.unwrap().len(), 10);
store.mark_inbound_group_sessions_as_backed_up(
"backup_1",
&(0..10).map(|i| session_info(&sessions[i])).collect::<Vec<_>>(),
).await.expect("Failed to mark sessions as backed up");

// Given we have backed up our session
store
.mark_inbound_group_sessions_as_backed_up("bkpver1", &[session_info(&session)])
.await
.expect("Failed to mark_inbound_group_sessions_as_backed_up.");

assert_eq!(store.inbound_group_session_counts(Some("bkpver1")).await.unwrap().total, 1);
assert_eq!(store.inbound_group_session_counts(Some("bkpver1")).await.unwrap().backed_up, 1);
// Sanity: none need backing up to the same backup
{
let to_back_up_old = store.inbound_group_sessions_for_backup("backup_1", 10).await.unwrap();
assert_eq!(to_back_up_old.len(), 0);
}

// Sanity: before resetting, we have nothing to back up
let to_back_up = store.inbound_group_sessions_for_backup("bkpver1", 1).await.unwrap();
assert_eq!(to_back_up, vec![]);
// Some stores ignore backup_version and just reset when you tell them to. Tell
// them here.
store.reset_backup_state().await.expect("reset failed");

// When we reset the backup
store.reset_backup_state().await.unwrap();
// When we ask what needs backing up to a different backup version
let to_back_up = store.inbound_group_sessions_for_backup("backup_02", 10).await.unwrap();

// Then after resetting, even if we supply the same backup version number, we need
// to back up the session
let to_back_up = store.inbound_group_sessions_for_backup("bkpver1", 1).await.unwrap();
assert_eq!(to_back_up, vec![session]);
// Then the answer is everything
let needs_backing_up = |i: usize| to_back_up.iter().any(|s| s.session_id() == sessions[i].session_id());
assert!(needs_backing_up(0));
assert!(needs_backing_up(1));
assert!(needs_backing_up(8));
assert!(needs_backing_up(9));
assert_eq!(to_back_up.len(), 10);
}

#[async_test]
Expand Down
Loading