From d2c7639d3c93baf5697ce459ca2ebf05d95bd498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Sun, 30 Jun 2024 12:27:36 +0200 Subject: [PATCH] verification: Get user crypto identity remotely if we do not have it locally --- src/session/model/user.rs | 83 +++++++++++++++++-- .../model/verification/verification_list.rs | 2 +- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/session/model/user.rs b/src/session/model/user.rs index 3e05c87b..e2e27dee 100644 --- a/src/session/model/user.rs +++ b/src/session/model/user.rs @@ -146,9 +146,15 @@ impl User { obj } - /// Get the cryptographic identity (aka cross-signing identity) of this - /// user. - pub async fn crypto_identity(&self) -> Option { + /// Get the local cryptographic identity (aka cross-signing identity) of + /// this user. + /// + /// Locally, we should always have the crypto identity of our own user and + /// of users with whom we share an encrypted room. + /// + /// To get the crypto identity of a user with whom we do not share an + /// encrypted room, use [`Self::ensure_crypto_identity()`]. + pub async fn local_crypto_identity(&self) -> Option { let encryption = self.session().client().encryption(); let user_id = self.user_id().clone(); let handle = spawn_tokio!(async move { encryption.get_user_identity(&user_id).await }); @@ -156,7 +162,67 @@ impl User { match handle.await.unwrap() { Ok(identity) => identity, Err(error) => { - error!("Could not find crypto identity: {error}"); + error!("Could not get local crypto identity: {error}"); + None + } + } + } + + /// Get the cryptographic identity (aka cross-signing identity) of this + /// user. + /// + /// First, we try to get the local crypto identity if we are sure that it is + /// up-to-date. If we do not have the crypto identity locally, we request it + /// from the homeserver. + pub async fn ensure_crypto_identity(&self) -> Option { + let session = self.session(); + let encryption = session.client().encryption(); + let user_id = self.user_id(); + + // First, see if we should have an updated crypto identity for the user locally. + // When we get the remote crypto identity of a user manually, it is cached + // locally but it is not kept up-to-date unless the user is tracked. That's why + // it's important to only use the local crypto identity if the user is tracked. + let should_have_local = if user_id == session.user_id() { + true + } else { + // We should have the updated user identity locally for tracked users. + let encryption_clone = encryption.clone(); + let handle = spawn_tokio!(async move { encryption_clone.tracked_users().await }); + + match handle.await.unwrap() { + Ok(tracked_users) => { + tracing::debug!("Got tracked users"); + tracked_users.contains(user_id) + } + Err(error) => { + error!("Could not get tracked users: {error}"); + // We are not sure, but let us try to get the local user identity first. + true + } + } + }; + tracing::debug!("should_have_local: {should_have_local:?}"); + // Try to get the local crypto identity. + if should_have_local { + if let Some(identity) = self.local_crypto_identity().await { + tracing::debug!("Local identity: {identity:?}"); + return Some(identity); + } + } + + // Now, try to request the crypto identity from the homeserver. + let user_id_clone = user_id.clone(); + let handle = + spawn_tokio!(async move { encryption.request_user_identity(&user_id_clone).await }); + + match handle.await.unwrap() { + Ok(identity) => { + tracing::debug!("remote identity: {identity:?}"); + identity + } + Err(error) => { + error!("Could not request remote crypto identity: {error}"); None } } @@ -176,7 +242,11 @@ impl User { #[weak(rename_to = obj)] self, async move { - let verified = obj.crypto_identity().await.is_some_and(|i| i.is_verified()); + // If a user is verified, we should have their crypto identity locally. + let verified = obj + .local_crypto_identity() + .await + .is_some_and(|i| i.is_verified()); if verified == obj.verified() { return; @@ -230,8 +300,7 @@ impl User { /// Get or create a direct chat with this user. /// - /// If there is no existing direct chat, a new one is created. If a direct - /// chat exists but the other user has left the room, we re-invite them. + /// If there is no existing direct chat, a new one is created. pub async fn get_or_create_direct_chat(&self) -> Result { let user_id = self.user_id(); diff --git a/src/session/model/verification/verification_list.rs b/src/session/model/verification/verification_list.rs index 1d04e933..42cf20cc 100644 --- a/src/session/model/verification/verification_list.rs +++ b/src/session/model/verification/verification_list.rs @@ -329,7 +329,7 @@ impl VerificationList { let supported_methods = load_supported_verification_methods().await; - let Some(identity) = user.crypto_identity().await else { + let Some(identity) = user.ensure_crypto_identity().await else { error!("Could not create identity verification: cryptographic identity not found"); return Err(()); };