From 7ff2f7537c055613946e0c441abb25ae17a5a46b Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 2 Aug 2024 12:27:18 +0200 Subject: [PATCH] crypto: Verified identity changes - Migration of existing data --- .../matrix-sdk-crypto/src/identities/user.rs | 5 +- crates/matrix-sdk-crypto/src/machine.rs | 77 +++++++++++++++++-- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/identities/user.rs b/crates/matrix-sdk-crypto/src/identities/user.rs index 50e54059281..e87381cf5ab 100644 --- a/crates/matrix-sdk-crypto/src/identities/user.rs +++ b/crates/matrix-sdk-crypto/src/identities/user.rs @@ -494,7 +494,6 @@ impl TryFrom for OtherUserIdentityData { self_signing_key: Arc::new(v0.self_signing_key), // We migrate by pinning the current master key pinned_master_key: Arc::new(RwLock::new(v0.master_key)), - // TODO Migration verified_latch: Arc::new(false.into()), }) } @@ -505,7 +504,8 @@ impl TryFrom for OtherUserIdentityData { master_key: Arc::new(v1.master_key.clone()), self_signing_key: Arc::new(v1.self_signing_key), pinned_master_key: Arc::new(RwLock::new(v1.pinned_master_key)), - // TODO Migration + // Put it to false. There will be a migration to mark all users as dirty, so we + // will receive an update for the identity that will correctly set up the value. verified_latch: Arc::new(false.into()), }) } @@ -516,7 +516,6 @@ impl TryFrom for OtherUserIdentityData { master_key: Arc::new(v2.master_key.clone()), self_signing_key: Arc::new(v2.self_signing_key), pinned_master_key: Arc::new(RwLock::new(v2.pinned_master_key)), - // TODO Migration verified_latch: Arc::new(v2.verified_latch.into()), }) } diff --git a/crates/matrix-sdk-crypto/src/machine.rs b/crates/matrix-sdk-crypto/src/machine.rs index 0bfc44573ca..29f9fe7c812 100644 --- a/crates/matrix-sdk-crypto/src/machine.rs +++ b/crates/matrix-sdk-crypto/src/machine.rs @@ -147,6 +147,7 @@ impl std::fmt::Debug for OlmMachine { impl OlmMachine { const CURRENT_GENERATION_STORE_KEY: &'static str = "generation-counter"; + const HAS_MIGRATED_VERIFICATION_LATCH: &'static str = "HAS_MIGRATED_VERIFICATION_LATCH"; /// Create a new memory based OlmMachine. /// @@ -357,9 +358,40 @@ impl OlmMachine { let identity = Arc::new(Mutex::new(identity)); let store = Arc::new(CryptoStoreWrapper::new(user_id, store)); + + // FIXME: We might want in the future a more generic high-level data migration + // mechanism (at the store wrapper layer). + Self::migration_post_verified_latch_support(&store).await?; + Ok(OlmMachine::new_helper(device_id, store, static_account, identity, maybe_backup_key)) } + // The sdk now support verified identity change detection. + // This introduces a new local flag (`verified_latch` on + // `OtherUserIdentityData`). In order to ensure that this flag is up-to-date and + // for the sake of simplicity we force a re-download of tracked users by marking + // them as dirty. + // + // pub(crate) visibility for testing. + pub(crate) async fn migration_post_verified_latch_support( + store: &CryptoStoreWrapper, + ) -> Result<(), CryptoStoreError> { + let maybe_migrate_for_identity_verified_latch = + store.get_custom_value(Self::HAS_MIGRATED_VERIFICATION_LATCH).await?.is_none(); + if maybe_migrate_for_identity_verified_latch { + // We want to mark all tracked users as dirty to ensure the verified latch is + // set up correctly. + let tracked_user = store.load_tracked_users().await?; + let mut store_updates = Vec::with_capacity(tracked_user.len()); + tracked_user.iter().for_each(|tu| { + store_updates.push((tu.user_id.as_ref(), true)); + }); + store.save_tracked_users(&store_updates).await?; + store.set_custom_value(Self::HAS_MIGRATED_VERIFICATION_LATCH, vec![0]).await? + } + Ok(()) + } + /// Get the crypto store associated with this `OlmMachine` instance. pub fn store(&self) -> &Store { &self.inner.store @@ -804,11 +836,11 @@ impl OlmMachine { } #[instrument( - skip_all, - // This function is only ever called by add_room_key via - // handle_decrypted_to_device_event, so sender, sender_key, and algorithm are - // already recorded. - fields(room_id = ?content.room_id, session_id) + skip_all, + // This function is only ever called by add_room_key via + // handle_decrypted_to_device_event, so sender, sender_key, and algorithm are + // already recorded. + fields(room_id = ? content.room_id, session_id) )] async fn handle_key( &self, @@ -4991,4 +5023,39 @@ pub(crate) mod tests { .unwrap(); assert_matches!(thread_encryption_result, UnsignedDecryptionResult::Decrypted(_)); } + + #[async_test] + async fn test_verified_latch_migration() { + let store = MemoryStore::new(); + let account = vodozemac::olm::Account::new(); + + // put some tracked users + let bob_id = user_id!("@bob:localhost"); + let carol_id = user_id!("@carol:localhost"); + + // Mark them as not dirty + let to_track_not_dirty = vec![(bob_id, false), (carol_id, false)]; + store.save_tracked_users(&to_track_not_dirty).await.unwrap(); + + let alice = OlmMachine::with_store(user_id(), alice_device_id(), store, Some(account)) + .await + .unwrap(); + + // A migration should have occurred and all users should be marked as dirty + alice.store().load_tracked_users().await.unwrap().iter().for_each(|tu| { + assert!(tu.dirty); + }); + + // Ensure it does so only once + alice.store().save_tracked_users(&to_track_not_dirty).await.unwrap(); + + OlmMachine::migration_post_verified_latch_support(alice.store().crypto_store().as_ref()) + .await + .unwrap(); + + // Migration already done, so user should not be marked as dirty + alice.store().load_tracked_users().await.unwrap().iter().for_each(|tu| { + assert!(!tu.dirty); + }); + } }