From 92c7aab36279feb23a1fb6f44c9ec202649c6afb Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 7 Oct 2024 16:41:33 +0300 Subject: [PATCH 01/11] feat(ffi): add support for receiving and working with session verification requests fixup! feat(ffi): add support for receiving and working with session verification requests --- bindings/matrix-sdk-ffi/src/client.rs | 4 +- .../src/session_verification.rs | 100 ++++++++++++++---- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index f5f693bae21..6cf78b28ae5 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -213,10 +213,10 @@ impl Client { let session_verification_controller: Arc< tokio::sync::RwLock>, > = Default::default(); - let ctrl = session_verification_controller.clone(); + let controller = session_verification_controller.clone(); sdk_client.add_event_handler(move |ev: AnyToDeviceEvent| async move { - if let Some(session_verification_controller) = &*ctrl.clone().read().await { + if let Some(session_verification_controller) = &*controller.clone().read().await { session_verification_controller.process_to_device_message(ev).await; } else { debug!("received to-device message, but verification controller isn't ready"); diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index 655e92d7e5f..b40f951b3b8 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, RwLock}; -use anyhow::Context as _; use futures_util::StreamExt; use matrix_sdk::{ encryption::{ @@ -10,6 +9,8 @@ use matrix_sdk::{ }, ruma::events::{key::verification::VerificationMethod, AnyToDeviceEvent}, }; +use ruma::UserId; +use tracing::{error, info}; use super::RUNTIME; use crate::error::ClientError; @@ -39,6 +40,7 @@ pub enum SessionVerificationData { #[matrix_sdk_ffi_macros::export(callback_interface)] pub trait SessionVerificationControllerDelegate: Sync + Send { + fn did_receive_verification_request(&self, sender_id: String, flow_id: String); fn did_accept_verification_request(&self); fn did_start_sas_verification(&self); fn did_receive_verification_data(&self, data: SessionVerificationData); @@ -60,17 +62,43 @@ pub struct SessionVerificationController { #[matrix_sdk_ffi_macros::export] impl SessionVerificationController { - pub async fn is_verified(&self) -> Result { - let device = - self.encryption.get_own_device().await?.context("Our own device is missing")?; + pub fn set_delegate(&self, delegate: Option>) { + *self.delegate.write().unwrap() = delegate; + } + + pub async fn accept_verification_request( + &self, + sender_id: String, + flow_id: String, + ) -> Result<(), ClientError> { + let sender_id = UserId::parse(sender_id.clone())?; + + let verification_request = self + .encryption + .get_verification_request(&sender_id, flow_id) + .await + .ok_or(ClientError::new("Unknown session verification request"))?; + + verification_request.accept().await?; + + *self.verification_request.write().unwrap() = Some(verification_request); - Ok(device.is_cross_signed_by_owner()) + Ok(()) } - pub fn set_delegate(&self, delegate: Option>) { - *self.delegate.write().unwrap() = delegate; + /// Accept the previously acknowledged verification request + pub async fn accept_verification_request(&self) -> Result<(), ClientError> { + let verification_request = self.verification_request.read().unwrap().clone(); + + if let Some(verification_request) = verification_request { + let methods = vec![VerificationMethod::SasV1]; + verification_request.accept_with_methods(methods).await?; + } + + Ok(()) } + /// Request verification for the current device pub async fn request_verification(&self) -> Result<(), ClientError> { let methods = vec![VerificationMethod::SasV1]; let verification_request = self @@ -78,6 +106,7 @@ impl SessionVerificationController { .request_verification_with_methods(methods) .await .map_err(anyhow::Error::from)?; + *self.verification_request.write().unwrap() = Some(verification_request); Ok(()) @@ -150,34 +179,61 @@ impl SessionVerificationController { pub(crate) async fn process_to_device_message(&self, event: AnyToDeviceEvent) { match event { + AnyToDeviceEvent::KeyVerificationRequest(event) => { + info!("Received verification request: {:}", event.sender); + + let Some(request) = self + .encryption + .get_verification_request(&event.sender, &event.content.transaction_id) + .await + else { + error!("Failed retrieving verification request"); + return; + }; + + if !request.is_self_verification() { + info!("Received non-self verification request. Ignoring."); + return; + } + + if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_receive_verification_request( + request.other_user_id().into(), + request.flow_id().into(), + ); + } + } // TODO: Use the changes stream for this as well once we expose // VerificationRequest::changes() in the main crate. AnyToDeviceEvent::KeyVerificationStart(event) => { if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) { return; } - if let Some(verification) = self + + let Some(verification) = self .encryption .get_verification( self.user_identity.user_id(), event.content.transaction_id.as_str(), ) .await - { - if let Some(sas_verification) = verification.sas() { - *self.sas_verification.write().unwrap() = Some(sas_verification.clone()); - - if sas_verification.accept().await.is_ok() { - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_start_sas_verification() - } - - let delegate = self.delegate.clone(); - RUNTIME.spawn(Self::listen_to_changes(delegate, sas_verification)); - } else if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_fail() - } + else { + return; + }; + + let Some(sas_verification) = verification.sas() else { return }; + + *self.sas_verification.write().unwrap() = Some(sas_verification.clone()); + + if sas_verification.accept().await.is_ok() { + if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_start_sas_verification() } + + let delegate = self.delegate.clone(); + RUNTIME.spawn(Self::listen_to_changes(delegate, sas_verification)); + } else if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_fail() } } AnyToDeviceEvent::KeyVerificationReady(event) => { From 48db6c9185d06c18e6277517ba153bd935e2d6ce Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 9 Oct 2024 14:16:03 +0300 Subject: [PATCH 02/11] refactor(ffi): switch to using `VerificationRequest::changes` instead of direct to_device events. --- .../src/session_verification.rs | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index b40f951b3b8..2e173f60778 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -4,7 +4,7 @@ use futures_util::StreamExt; use matrix_sdk::{ encryption::{ identities::UserIdentity, - verification::{SasState, SasVerification, VerificationRequest}, + verification::{SasState, SasVerification, VerificationRequest, VerificationRequestState}, Encryption, }, ruma::events::{key::verification::VerificationMethod, AnyToDeviceEvent}, @@ -107,7 +107,13 @@ impl SessionVerificationController { .await .map_err(anyhow::Error::from)?; - *self.verification_request.write().unwrap() = Some(verification_request); + *self.verification_request.write().unwrap() = Some(verification_request.clone()); + + RUNTIME.spawn(Self::listen_to_verification_request_changes( + verification_request, + self.sas_verification.clone(), + self.delegate.clone(), + )); Ok(()) } @@ -125,7 +131,7 @@ impl SessionVerificationController { } let delegate = self.delegate.clone(); - RUNTIME.spawn(Self::listen_to_changes(delegate, verification)); + RUNTIME.spawn(Self::listen_to_sas_verification_changes(verification, delegate)); } _ => { if let Some(delegate) = &*self.delegate.read().unwrap() { @@ -203,60 +209,57 @@ impl SessionVerificationController { ); } } - // TODO: Use the changes stream for this as well once we expose - // VerificationRequest::changes() in the main crate. - AnyToDeviceEvent::KeyVerificationStart(event) => { - if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) { - return; - } + _ => (), + } + } - let Some(verification) = self - .encryption - .get_verification( - self.user_identity.user_id(), - event.content.transaction_id.as_str(), - ) - .await - else { - return; - }; + async fn listen_to_verification_request_changes( + verification_request: VerificationRequest, + sas_verification: Arc>>, + delegate: Delegate, + ) { + let mut stream = verification_request.changes(); - let Some(sas_verification) = verification.sas() else { return }; + while let Some(state) = stream.next().await { + match state { + VerificationRequestState::Transitioned { verification } => { + let Some(verification) = verification.sas() else { + error!("Invalid, non-sas verification flow. Returning."); + return; + }; - *self.sas_verification.write().unwrap() = Some(sas_verification.clone()); + *sas_verification.write().unwrap() = Some(verification.clone()); - if sas_verification.accept().await.is_ok() { - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_start_sas_verification() - } + if verification.accept().await.is_ok() { + if let Some(delegate) = &*delegate.read().unwrap() { + delegate.did_start_sas_verification() + } - let delegate = self.delegate.clone(); - RUNTIME.spawn(Self::listen_to_changes(delegate, sas_verification)); - } else if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_fail() + let delegate = delegate.clone(); + RUNTIME.spawn(Self::listen_to_sas_verification_changes( + verification, + delegate, + )); + } else if let Some(delegate) = &*delegate.read().unwrap() { + delegate.did_fail() + } } - } - AnyToDeviceEvent::KeyVerificationReady(event) => { - if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) { - return; + VerificationRequestState::Ready { .. } => { + if let Some(delegate) = &*delegate.read().unwrap() { + delegate.did_accept_verification_request() + } } - - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_accept_verification_request() + VerificationRequestState::Cancelled(..) => { + if let Some(delegate) = &*delegate.read().unwrap() { + delegate.did_cancel(); + } } + _ => {} } - _ => (), - } - } - - fn is_transaction_id_valid(&self, transaction_id: String) -> bool { - match &*self.verification_request.read().unwrap() { - Some(verification) => verification.flow_id() == transaction_id, - None => false, } } - async fn listen_to_changes(delegate: Delegate, sas: SasVerification) { + async fn listen_to_sas_verification_changes(sas: SasVerification, delegate: Delegate) { let mut stream = sas.changes(); while let Some(state) = stream.next().await { From 7d2eec4783be3fb40c5408448237a5dcf6021fa5 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 9 Oct 2024 15:22:22 +0300 Subject: [PATCH 03/11] Expose mechanism for registering to verification updates before actually accepting one - allows handling remote cancellations on verification requests that have not yet been accepted --- .../src/session_verification.rs | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index 2e173f60778..7f51c1d740d 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -66,11 +66,10 @@ impl SessionVerificationController { *self.delegate.write().unwrap() = delegate; } - pub async fn accept_verification_request( - &self, - sender_id: String, - flow_id: String, - ) -> Result<(), ClientError> { + /// Set this particular request as the currently active one and register for events pertaining it. + /// * `sender_id` - The user requesting verification. + /// * `flow_id` - - The ID that uniquely identifies the verification flow. + pub async fn acknowledge_verification_request(&self, sender_id: String, flow_id: String) { let sender_id = UserId::parse(sender_id.clone())?; let verification_request = self @@ -79,9 +78,24 @@ impl SessionVerificationController { .await .ok_or(ClientError::new("Unknown session verification request"))?; - verification_request.accept().await?; + *self.verification_request.write().unwrap() = Some(verification_request.clone()); + + RUNTIME.spawn(Self::listen_to_verification_request_changes( + verification_request, + self.sas_verification.clone(), + self.delegate.clone(), + )); + + Ok(()) + } + + /// Accept the previously acknowledged verification request + pub async fn accept_verification_request(&self) -> Result<(), ClientError> { + let verification_request = self.verification_request.read().unwrap().clone(); - *self.verification_request.write().unwrap() = Some(verification_request); + if let Some(verification_request) = verification_request { + verification_request.accept().await?; + } Ok(()) } From 57179707bb2e8e819463f372c570dbf2a185e2ee Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 9 Oct 2024 15:32:33 +0300 Subject: [PATCH 04/11] Documentation + cleanup --- .../src/session_verification.rs | 76 ++++++++++--------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index 7f51c1d740d..fa6b56f77de 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -69,7 +69,11 @@ impl SessionVerificationController { /// Set this particular request as the currently active one and register for events pertaining it. /// * `sender_id` - The user requesting verification. /// * `flow_id` - - The ID that uniquely identifies the verification flow. - pub async fn acknowledge_verification_request(&self, sender_id: String, flow_id: String) { + pub async fn acknowledge_verification_request( + &self, + sender_id: String, + flow_id: String, + ) -> Result<(), ClientError> { let sender_id = UserId::parse(sender_id.clone())?; let verification_request = self @@ -89,17 +93,6 @@ impl SessionVerificationController { Ok(()) } - /// Accept the previously acknowledged verification request - pub async fn accept_verification_request(&self) -> Result<(), ClientError> { - let verification_request = self.verification_request.read().unwrap().clone(); - - if let Some(verification_request) = verification_request { - verification_request.accept().await?; - } - - Ok(()) - } - /// Accept the previously acknowledged verification request pub async fn accept_verification_request(&self) -> Result<(), ClientError> { let verification_request = self.verification_request.read().unwrap().clone(); @@ -132,25 +125,28 @@ impl SessionVerificationController { Ok(()) } + /// Transition the current verification request into a SAS verification flow. pub async fn start_sas_verification(&self) -> Result<(), ClientError> { let verification_request = self.verification_request.read().unwrap().clone(); - if let Some(verification) = verification_request { - match verification.start_sas().await { - Ok(Some(verification)) => { - *self.sas_verification.write().unwrap() = Some(verification.clone()); + let Some(verification_request) = verification_request else { + return Err(ClientError::new("Verification request missing.")); + }; - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_start_sas_verification() - } + match verification_request.start_sas().await { + Ok(Some(verification)) => { + *self.sas_verification.write().unwrap() = Some(verification.clone()); - let delegate = self.delegate.clone(); - RUNTIME.spawn(Self::listen_to_sas_verification_changes(verification, delegate)); + if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_start_sas_verification() } - _ => { - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_fail() - } + + let delegate = self.delegate.clone(); + RUNTIME.spawn(Self::listen_to_sas_verification_changes(verification, delegate)); + } + _ => { + if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_fail() } } } @@ -158,31 +154,37 @@ impl SessionVerificationController { Ok(()) } + /// Confirm that the short auth strings match on both sides. pub async fn approve_verification(&self) -> Result<(), ClientError> { let sas_verification = self.sas_verification.read().unwrap().clone(); - if let Some(sas_verification) = sas_verification { - sas_verification.confirm().await?; - } - Ok(()) + let Some(sas_verification) = sas_verification else { + return Err(ClientError::new("SAS verification missing")); + }; + + Ok(sas_verification.confirm().await?) } + /// Reject the short auth string pub async fn decline_verification(&self) -> Result<(), ClientError> { let sas_verification = self.sas_verification.read().unwrap().clone(); - if let Some(sas_verification) = sas_verification { - sas_verification.mismatch().await?; - } - Ok(()) + let Some(sas_verification) = sas_verification else { + return Err(ClientError::new("SAS verification missing")); + }; + + Ok(sas_verification.mismatch().await?) } + /// Cancel the current verification request pub async fn cancel_verification(&self) -> Result<(), ClientError> { let verification_request = self.verification_request.read().unwrap().clone(); - if let Some(verification) = verification_request { - verification.cancel().await?; - } - Ok(()) + let Some(verification_request) = verification_request else { + return Err(ClientError::new("Verification request missing.")); + }; + + Ok(verification_request.cancel().await?) } } From 237fe772a6da86f8d8ff2e02fa4981550bcbfec0 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 10 Oct 2024 12:14:43 +0300 Subject: [PATCH 05/11] Fix a clippy warning re single matching --- .../src/session_verification.rs | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index fa6b56f77de..5b65b2c4436 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -200,32 +200,29 @@ impl SessionVerificationController { } pub(crate) async fn process_to_device_message(&self, event: AnyToDeviceEvent) { - match event { - AnyToDeviceEvent::KeyVerificationRequest(event) => { - info!("Received verification request: {:}", event.sender); - - let Some(request) = self - .encryption - .get_verification_request(&event.sender, &event.content.transaction_id) - .await - else { - error!("Failed retrieving verification request"); - return; - }; - - if !request.is_self_verification() { - info!("Received non-self verification request. Ignoring."); - return; - } + if let AnyToDeviceEvent::KeyVerificationRequest(event) = event { + info!("Received verification request: {:}", event.sender); + + let Some(request) = self + .encryption + .get_verification_request(&event.sender, &event.content.transaction_id) + .await + else { + error!("Failed retrieving verification request"); + return; + }; + + if !request.is_self_verification() { + info!("Received non-self verification request. Ignoring."); + return; + } - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_receive_verification_request( - request.other_user_id().into(), - request.flow_id().into(), - ); - } + if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_receive_verification_request( + request.other_user_id().into(), + request.flow_id().into(), + ); } - _ => (), } } From 5ab023d10c00d5e495e5da1e0751e9596649b8c2 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 10 Oct 2024 12:14:11 +0300 Subject: [PATCH 06/11] feat(crypto): store a copy of the requesting `DeviceData` within `VerificationRequestState`s --- .../src/verification/machine.rs | 8 +++ .../src/verification/requests.rs | 68 ++++++++++++++----- .../src/encryption/verification/requests.rs | 12 ++-- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/verification/machine.rs b/crates/matrix-sdk-crypto/src/verification/machine.rs index c82d3b7867f..19033209e6e 100644 --- a/crates/matrix-sdk-crypto/src/verification/machine.rs +++ b/crates/matrix-sdk-crypto/src/verification/machine.rs @@ -370,12 +370,20 @@ impl VerificationMachine { return Ok(()); } + let Some(device_data) = + self.store.get_device(event.sender(), r.from_device()).await? + else { + warn!("Could not retrieve the device data for the incoming verification request, ignoring it"); + return Ok(()); + }; + let request = VerificationRequest::from_request( self.verifications.clone(), self.store.clone(), event.sender(), flow_id, r, + device_data, ); self.insert_request(request); diff --git a/crates/matrix-sdk-crypto/src/verification/requests.rs b/crates/matrix-sdk-crypto/src/verification/requests.rs index 94c2b599d59..9c1c4e202be 100644 --- a/crates/matrix-sdk-crypto/src/verification/requests.rs +++ b/crates/matrix-sdk-crypto/src/verification/requests.rs @@ -52,8 +52,8 @@ use super::{ CancelInfo, Cancelled, FlowId, Verification, VerificationStore, }; use crate::{ - olm::StaticAccountData, CryptoStoreError, OutgoingVerificationRequest, RoomMessageRequest, Sas, - ToDeviceRequest, + olm::StaticAccountData, CryptoStoreError, DeviceData, OutgoingVerificationRequest, + RoomMessageRequest, Sas, ToDeviceRequest, }; const SUPPORTED_METHODS: &[VerificationMethod] = &[ @@ -78,9 +78,9 @@ pub enum VerificationRequestState { /// The verification methods supported by the sender. their_methods: Vec, - /// The device ID of the device that responded to the verification + /// The device data of the device that responded to the verification /// request. - other_device_id: OwnedDeviceId, + other_device_data: DeviceData, }, /// The verification request is ready to start a verification flow. Ready { @@ -116,7 +116,7 @@ impl From<&InnerRequest> for VerificationRequestState { } InnerRequest::Requested(s) => Self::Requested { their_methods: s.state.their_methods.to_owned(), - other_device_id: s.state.other_device_id.to_owned(), + other_device_data: s.state.other_device_data.to_owned(), }, InnerRequest::Ready(s) => Self::Ready { their_methods: s.state.their_methods.to_owned(), @@ -281,7 +281,7 @@ impl VerificationRequest { /// The id of the other device that is participating in this verification. pub fn other_device_id(&self) -> Option { match &*self.inner.read() { - InnerRequest::Requested(r) => Some(r.state.other_device_id.to_owned()), + InnerRequest::Requested(r) => Some(r.state.other_device_data.device_id().to_owned()), InnerRequest::Ready(r) => Some(r.state.other_device_id.to_owned()), InnerRequest::Transitioned(r) => Some(r.state.ready.other_device_id.to_owned()), InnerRequest::Created(_) @@ -466,13 +466,21 @@ impl VerificationRequest { sender: &UserId, flow_id: FlowId, content: &RequestContent<'_>, + device_data: DeviceData, ) -> Self { let account = store.account.clone(); Self { verification_cache: cache.clone(), inner: SharedObservable::new(InnerRequest::Requested( - RequestState::from_request_event(cache, store, sender, &flow_id, content), + RequestState::from_request_event( + cache, + store, + sender, + &flow_id, + content, + device_data, + ), )), account, other_user_id: sender.into(), @@ -889,7 +897,7 @@ impl InnerRequest { match self { InnerRequest::Created(_) => DeviceIdOrAllDevices::AllDevices, InnerRequest::Requested(r) => { - DeviceIdOrAllDevices::DeviceId(r.state.other_device_id.to_owned()) + DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned()) } InnerRequest::Ready(r) => { DeviceIdOrAllDevices::DeviceId(r.state.other_device_id.to_owned()) @@ -1061,8 +1069,9 @@ struct Requested { /// The verification methods supported by the sender. pub their_methods: Vec, - /// The device ID of the device that responded to the verification request. - pub other_device_id: OwnedDeviceId, + /// The device data of the device that responded to the verification + /// request. + pub other_device_data: DeviceData, } impl RequestState { @@ -1072,6 +1081,7 @@ impl RequestState { sender: &UserId, flow_id: &FlowId, content: &RequestContent<'_>, + device_data: DeviceData, ) -> RequestState { // TODO only create this if we support the methods RequestState { @@ -1081,7 +1091,7 @@ impl RequestState { other_user_id: sender.to_owned(), state: Requested { their_methods: content.methods().to_owned(), - other_device_id: content.from_device().into(), + other_device_data: device_data, }, } } @@ -1105,7 +1115,7 @@ impl RequestState { state: Ready { their_methods: self.state.their_methods, our_methods: methods.clone(), - other_device_id: self.state.other_device_id.clone(), + other_device_id: self.state.other_device_data.device_id().to_owned(), }, }; @@ -1652,6 +1662,12 @@ mod tests { None, ); + let device_data = alice_store + .get_device(&bob_store.account.user_id, &bob_store.account.device_id) + .await + .unwrap() + .expect("Missing device data"); + let flow_id = FlowId::InRoom(room_id, event_id); let bob_request = VerificationRequest::new( @@ -1674,6 +1690,7 @@ mod tests { bob_id(), flow_id, &(&content).into(), + device_data, ); assert_matches!(alice_request.state(), VerificationRequestState::Requested { .. }); @@ -1698,7 +1715,7 @@ mod tests { // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), None); - let alice_request = build_incoming_verification_request(&alice_store, &bob_request); + let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; let outgoing_request = alice_request.cancel().unwrap(); @@ -1740,6 +1757,13 @@ mod tests { alice_id(), None, ); + + let device_data = alice_store + .get_device(&bob_store.account.user_id, &bob_store.account.device_id) + .await + .unwrap() + .expect("Missing device data"); + let flow_id = FlowId::from((room_id, event_id)); let bob_request = VerificationRequest::new( @@ -1758,6 +1782,7 @@ mod tests { bob_id(), flow_id, &(&content).into(), + device_data, ); do_accept_request(&alice_request, &bob_request, None); @@ -1791,7 +1816,7 @@ mod tests { // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), None); - let alice_request = build_incoming_verification_request(&alice_store, &bob_request); + let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; do_accept_request(&alice_request, &bob_request, None); let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); @@ -1829,7 +1854,7 @@ mod tests { alice_id(), Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]), ); - let alice_request = build_incoming_verification_request(&alice_store, &bob_request); + let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; do_accept_request( &alice_request, &bob_request, @@ -1876,7 +1901,7 @@ mod tests { // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods())); - let alice_request = build_incoming_verification_request(&alice_store, &bob_request); + let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; do_accept_request(&alice_request, &bob_request, Some(all_methods())); // Each side can start its own QR verification flow by generating QR code @@ -1921,7 +1946,7 @@ mod tests { // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods())); - let alice_request = build_incoming_verification_request(&alice_store, &bob_request); + let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; do_accept_request(&alice_request, &bob_request, Some(all_methods())); // Bob generates a QR code @@ -1996,7 +2021,7 @@ mod tests { /// Tells the outgoing request to generate an `m.key.verification.request` /// to-device message, and uses it to build a new request for the incoming /// side. - fn build_incoming_verification_request( + async fn build_incoming_verification_request( verification_store: &VerificationStore, outgoing_request: &VerificationRequest, ) -> VerificationRequest { @@ -2004,12 +2029,19 @@ mod tests { let content: OutgoingContent = request.try_into().unwrap(); let content = RequestContent::try_from(&content).unwrap(); + let device_data = verification_store + .get_device(outgoing_request.own_user_id(), content.from_device()) + .await + .unwrap() + .expect("Missing device data"); + VerificationRequest::from_request( VerificationCache::new(), verification_store.clone(), outgoing_request.own_user_id(), outgoing_request.flow_id().clone(), &content, + device_data, ) } diff --git a/crates/matrix-sdk/src/encryption/verification/requests.rs b/crates/matrix-sdk/src/encryption/verification/requests.rs index 30e56f1f177..0e6fb35f510 100644 --- a/crates/matrix-sdk/src/encryption/verification/requests.rs +++ b/crates/matrix-sdk/src/encryption/verification/requests.rs @@ -13,7 +13,9 @@ // limitations under the License. use futures_util::{Stream, StreamExt}; -use matrix_sdk_base::crypto::{CancelInfo, VerificationRequest as BaseVerificationRequest}; +use matrix_sdk_base::crypto::{ + CancelInfo, DeviceData, VerificationRequest as BaseVerificationRequest, +}; use ruma::{events::key::verification::VerificationMethod, OwnedDeviceId, RoomId}; #[cfg(feature = "qrcode")] @@ -41,9 +43,9 @@ pub enum VerificationRequestState { /// The verification methods supported by the sender. their_methods: Vec, - /// The device ID of the device that responded to the verification + /// The device data of the device that responded to the verification /// request. - other_device_id: OwnedDeviceId, + other_device_data: DeviceData, }, /// The verification request is ready to start a verification flow. Ready { @@ -218,8 +220,8 @@ impl VerificationRequest { match state { Created { our_methods } => VerificationRequestState::Created { our_methods }, - Requested { their_methods, other_device_id } => { - VerificationRequestState::Requested { their_methods, other_device_id } + Requested { their_methods, other_device_data } => { + VerificationRequestState::Requested { their_methods, other_device_data } } Ready { their_methods, our_methods, other_device_id } => { VerificationRequestState::Ready { their_methods, our_methods, other_device_id } From c43c10ae1b32c5e3e5780e8cc14216a9fdb8bf7e Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 10 Oct 2024 18:44:22 +0300 Subject: [PATCH 07/11] Expose requesting device details to the final client --- .../src/session_verification.rs | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index 5b65b2c4436..6cc4dea8b76 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -38,9 +38,20 @@ pub enum SessionVerificationData { Decimals { values: Vec }, } +/// Details about the incoming verification request +#[derive(Debug, uniffi::Record)] +pub struct SessionVerificationRequestDetails { + sender_id: String, + flow_id: String, + device_id: String, + display_name: Option, + /// First time this device was seen in milliseconds since epoch. + first_seen_timestamp: u64, +} + #[matrix_sdk_ffi_macros::export(callback_interface)] pub trait SessionVerificationControllerDelegate: Sync + Send { - fn did_receive_verification_request(&self, sender_id: String, flow_id: String); + fn did_receive_verification_request(&self, details: SessionVerificationRequestDetails); fn did_accept_verification_request(&self); fn did_start_sas_verification(&self); fn did_receive_verification_data(&self, data: SessionVerificationData); @@ -66,7 +77,8 @@ impl SessionVerificationController { *self.delegate.write().unwrap() = delegate; } - /// Set this particular request as the currently active one and register for events pertaining it. + /// Set this particular request as the currently active one and register for + /// events pertaining it. /// * `sender_id` - The user requesting verification. /// * `flow_id` - - The ID that uniquely identifies the verification flow. pub async fn acknowledge_verification_request( @@ -125,7 +137,8 @@ impl SessionVerificationController { Ok(()) } - /// Transition the current verification request into a SAS verification flow. + /// Transition the current verification request into a SAS verification + /// flow. pub async fn start_sas_verification(&self) -> Result<(), ClientError> { let verification_request = self.verification_request.read().unwrap().clone(); @@ -217,11 +230,20 @@ impl SessionVerificationController { return; } + let VerificationRequestState::Requested { other_device_data, .. } = request.state() + else { + error!("Received key verification event but the request is in the wrong state."); + return; + }; + if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_receive_verification_request( - request.other_user_id().into(), - request.flow_id().into(), - ); + delegate.did_receive_verification_request(SessionVerificationRequestDetails { + sender_id: request.other_user_id().into(), + flow_id: request.flow_id().into(), + device_id: other_device_data.device_id().into(), + display_name: other_device_data.display_name().map(str::to_string), + first_seen_timestamp: other_device_data.first_time_seen_ts().get().into(), + }); } } } From 290ea3e5caea8a28594e33e0952a046854aafff0 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 15 Oct 2024 17:13:27 +0300 Subject: [PATCH 08/11] fix(crypto): fix incorrect `VerificationMachine` tests - the tests used to incorrectly wrap the to-device content into an event as if it was sent by alice instead of bob --- .../src/verification/machine.rs | 17 +++++++++-------- .../matrix-sdk-crypto/src/verification/mod.rs | 2 +- .../src/verification/sas/mod.rs | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/verification/machine.rs b/crates/matrix-sdk-crypto/src/verification/machine.rs index 19033209e6e..864800f1a97 100644 --- a/crates/matrix-sdk-crypto/src/verification/machine.rs +++ b/crates/matrix-sdk-crypto/src/verification/machine.rs @@ -341,6 +341,7 @@ impl VerificationMachine { match &content { AnyVerificationContent::Request(r) => { info!( + sender = ?event.sender(), from_device = r.from_device().as_str(), "Received a new verification request", ); @@ -733,12 +734,12 @@ mod tests { let content: OutgoingContent = request.try_into().unwrap(); machine - .receive_any_event(&wrap_any_to_device_content(bob_request.other_user(), content)) + .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content)) .await .unwrap(); let alice_request = - machine.get_request(bob_request.other_user(), bob_request.flow_id().as_str()).unwrap(); + machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap(); // We're not yet cancelled. assert!(!alice_request.is_cancelled()); @@ -757,12 +758,12 @@ mod tests { let content: OutgoingContent = request.try_into().unwrap(); machine - .receive_any_event(&wrap_any_to_device_content(bob_request.other_user(), content)) + .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content)) .await .unwrap(); let second_request = - machine.get_request(bob_request.other_user(), bob_request.flow_id().as_str()).unwrap(); + machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap(); // Make sure we fetched the new one. assert_eq!(second_request.flow_id().as_str(), second_transaction_id); @@ -795,12 +796,12 @@ mod tests { let content: OutgoingContent = request.try_into().unwrap(); machine - .receive_any_event(&wrap_any_to_device_content(bob_request.other_user(), content)) + .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content)) .await .unwrap(); let first_request = - machine.get_request(bob_request.other_user(), bob_request.flow_id().as_str()).unwrap(); + machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap(); // We're not yet cancelled. assert!(!first_request.is_cancelled()); @@ -819,12 +820,12 @@ mod tests { let content: OutgoingContent = request.try_into().unwrap(); machine - .receive_any_event(&wrap_any_to_device_content(bob_request.other_user(), content)) + .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content)) .await .unwrap(); let second_request = - machine.get_request(bob_request.other_user(), bob_request.flow_id().as_str()).unwrap(); + machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap(); // None of the requests are cancelled assert!(!first_request.is_cancelled()); diff --git a/crates/matrix-sdk-crypto/src/verification/mod.rs b/crates/matrix-sdk-crypto/src/verification/mod.rs index 6d48d38e684..1598114fb06 100644 --- a/crates/matrix-sdk-crypto/src/verification/mod.rs +++ b/crates/matrix-sdk-crypto/src/verification/mod.rs @@ -819,7 +819,7 @@ pub(crate) mod tests { } pub fn bob_device_id() -> &'static DeviceId { - device_id!("BOBDEVCIE") + device_id!("BOBDEVICE") } pub(crate) async fn setup_stores() -> (Account, VerificationStore, Account, VerificationStore) { diff --git a/crates/matrix-sdk-crypto/src/verification/sas/mod.rs b/crates/matrix-sdk-crypto/src/verification/sas/mod.rs index c9a3acf961f..caf43a249ab 100644 --- a/crates/matrix-sdk-crypto/src/verification/sas/mod.rs +++ b/crates/matrix-sdk-crypto/src/verification/sas/mod.rs @@ -899,7 +899,7 @@ mod tests { } fn bob_device_id() -> &'static DeviceId { - device_id!("BOBDEVCIE") + device_id!("BOBDEVICE") } fn machine_pair_test_helper() -> (VerificationStore, DeviceData, VerificationStore, DeviceData) From 5c3ef6bc4741dd87a47947a6811456a88ffbe9ee Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 21 Oct 2024 12:40:33 +0300 Subject: [PATCH 09/11] chore(tests): fix verification integration tests following changes to the data associated with `VerificationRequestState::Requested` --- testing/matrix-sdk-integration-testing/src/tests/e2ee.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testing/matrix-sdk-integration-testing/src/tests/e2ee.rs b/testing/matrix-sdk-integration-testing/src/tests/e2ee.rs index 233dbc49e7d..1c72ae8c17a 100644 --- a/testing/matrix-sdk-integration-testing/src/tests/e2ee.rs +++ b/testing/matrix-sdk-integration-testing/src/tests/e2ee.rs @@ -70,6 +70,7 @@ async fn test_mutual_sas_verification() -> Result<()> { bob.get_room(room_id).unwrap().join().await?; alice.sync_once().await?; + bob.sync_once().await?; warn!("alice and bob are both aware of each other in the e2ee room"); @@ -331,6 +332,7 @@ async fn test_mutual_qrcode_verification() -> Result<()> { bob.get_room(room_id).unwrap().join().await?; alice.sync_once().await?; + bob.sync_once().await?; warn!("alice and bob are both aware of each other in the e2ee room"); @@ -841,6 +843,9 @@ async fn test_secret_gossip_after_interactive_verification() -> Result<()> { // The first client is not verified from the point of view of the second client. assert!(!seconds_first_device.is_verified()); + // Make the first client aware of the device we're requesting verification for + first_client.sync_once().await?; + // Let's send out a request to verify with each other. let seconds_verification_request = seconds_first_device.request_verification().await?; let flow_id = seconds_verification_request.flow_id(); From 019bbf67c6ddc53727158e4116c5ba2a78260503 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 24 Oct 2024 09:49:51 +0300 Subject: [PATCH 10/11] Pass a copy of the other `DeviceData` in between the (Requested, Ready) and (Created, Ready) states --- .../matrix-sdk-crypto-ffi/src/verification.rs | 2 +- .../src/verification/machine.rs | 8 +- .../src/verification/requests.rs | 162 +++++++++--------- .../src/encryption/verification/requests.rs | 10 +- 4 files changed, 90 insertions(+), 92 deletions(-) diff --git a/bindings/matrix-sdk-crypto-ffi/src/verification.rs b/bindings/matrix-sdk-crypto-ffi/src/verification.rs index e8c31c4a3a8..c522a1ffa16 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/verification.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/verification.rs @@ -752,7 +752,7 @@ impl VerificationRequest { RustVerificationRequestState::Ready { their_methods, our_methods, - other_device_id: _, + other_device_data: _, } => VerificationRequestState::Ready { their_methods: their_methods.iter().map(|m| m.to_string()).collect(), our_methods: our_methods.iter().map(|m| m.to_string()).collect(), diff --git a/crates/matrix-sdk-crypto/src/verification/machine.rs b/crates/matrix-sdk-crypto/src/verification/machine.rs index 864800f1a97..892fa255183 100644 --- a/crates/matrix-sdk-crypto/src/verification/machine.rs +++ b/crates/matrix-sdk-crypto/src/verification/machine.rs @@ -412,7 +412,13 @@ impl VerificationMachine { }; if request.flow_id() == &flow_id { - request.receive_ready(event.sender(), c); + if let Some(device_data) = + self.store.get_device(event.sender(), c.from_device()).await? + { + request.receive_ready(event.sender(), c, device_data); + } else { + warn!("Could not retrieve the data for the accepting device, ignoring it"); + } } else { flow_id_mismatch(); } diff --git a/crates/matrix-sdk-crypto/src/verification/requests.rs b/crates/matrix-sdk-crypto/src/verification/requests.rs index 9c1c4e202be..7b8d7a27338 100644 --- a/crates/matrix-sdk-crypto/src/verification/requests.rs +++ b/crates/matrix-sdk-crypto/src/verification/requests.rs @@ -90,9 +90,9 @@ pub enum VerificationRequestState { /// The verification methods supported by the us. our_methods: Vec, - /// The device ID of the device that responded to the verification + /// The device data of the device that responded to the verification /// request. - other_device_id: OwnedDeviceId, + other_device_data: DeviceData, }, /// The verification request has transitioned into a concrete verification /// flow. For example it transitioned into the emoji based SAS @@ -121,7 +121,7 @@ impl From<&InnerRequest> for VerificationRequestState { InnerRequest::Ready(s) => Self::Ready { their_methods: s.state.their_methods.to_owned(), our_methods: s.state.our_methods.to_owned(), - other_device_id: s.state.other_device_id.to_owned(), + other_device_data: s.state.other_device_data.to_owned(), }, InnerRequest::Transitioned(s) => { Self::Transitioned { verification: s.state.verification.to_owned() } @@ -282,8 +282,10 @@ impl VerificationRequest { pub fn other_device_id(&self) -> Option { match &*self.inner.read() { InnerRequest::Requested(r) => Some(r.state.other_device_data.device_id().to_owned()), - InnerRequest::Ready(r) => Some(r.state.other_device_id.to_owned()), - InnerRequest::Transitioned(r) => Some(r.state.ready.other_device_id.to_owned()), + InnerRequest::Ready(r) => Some(r.state.other_device_data.device_id().to_owned()), + InnerRequest::Transitioned(r) => { + Some(r.state.ready.other_device_data.device_id().to_owned()) + } InnerRequest::Created(_) | InnerRequest::Passive(_) | InnerRequest::Done(_) @@ -666,12 +668,18 @@ impl VerificationRequest { Some(ToDeviceRequest::for_recipients(recipient, recip_devices, &c, TransactionId::new())) } - pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent<'_>) { + pub(crate) fn receive_ready( + &self, + sender: &UserId, + content: &ReadyContent<'_>, + from_device_data: DeviceData, + ) { let mut guard = self.inner.write(); match &*guard { InnerRequest::Created(s) => { - let new_value = InnerRequest::Ready(s.clone().into_ready(sender, content)); + let new_value = + InnerRequest::Ready(s.clone().into_ready(sender, content, from_device_data)); ObservableWriteGuard::set(&mut guard, new_value); if let Some(request) = @@ -900,11 +908,11 @@ impl InnerRequest { DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned()) } InnerRequest::Ready(r) => { - DeviceIdOrAllDevices::DeviceId(r.state.other_device_id.to_owned()) - } - InnerRequest::Transitioned(r) => { - DeviceIdOrAllDevices::DeviceId(r.state.ready.other_device_id.to_owned()) + DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned()) } + InnerRequest::Transitioned(r) => DeviceIdOrAllDevices::DeviceId( + r.state.ready.other_device_data.device_id().to_owned(), + ), InnerRequest::Passive(_) => DeviceIdOrAllDevices::AllDevices, InnerRequest::Done(_) => DeviceIdOrAllDevices::AllDevices, InnerRequest::Cancelled(_) => DeviceIdOrAllDevices::AllDevices, @@ -1042,7 +1050,12 @@ impl RequestState { } } - fn into_ready(self, _sender: &UserId, content: &ReadyContent<'_>) -> RequestState { + fn into_ready( + self, + _sender: &UserId, + content: &ReadyContent<'_>, + from_device_data: DeviceData, + ) -> RequestState { // TODO check the flow id, and that the methods match what we suggested. RequestState { flow_id: self.flow_id, @@ -1052,7 +1065,7 @@ impl RequestState { state: Ready { their_methods: content.methods().to_owned(), our_methods: self.state.our_methods, - other_device_id: content.from_device().into(), + other_device_data: from_device_data, }, } } @@ -1115,7 +1128,7 @@ impl RequestState { state: Ready { their_methods: self.state.their_methods, our_methods: methods.clone(), - other_device_id: self.state.other_device_data.device_id().to_owned(), + other_device_data: self.state.other_device_data, }, }; @@ -1153,8 +1166,9 @@ struct Ready { /// The verification methods supported by the us. pub our_methods: Vec, - /// The device ID of the device that responded to the verification request. - pub other_device_id: OwnedDeviceId, + /// The device data of the device that responded to the verification + /// request. + pub other_device_data: DeviceData, } #[cfg(feature = "qrcode")] @@ -1168,7 +1182,7 @@ async fn scan_qr_code( let verification = QrVerification::from_scan( request_state.store.to_owned(), request_state.other_user_id.to_owned(), - state.other_device_id.to_owned(), + state.other_device_data.device_id().to_owned(), request_state.flow_id.as_ref().to_owned(), data, we_started, @@ -1207,21 +1221,7 @@ async fn generate_qr_code( return Ok(None); } - let Some(device) = request_state - .store - .get_device(&request_state.other_user_id, &state.other_device_id) - .await? - else { - warn!( - user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, - "Can't create a QR code, the device that accepted the \ - verification doesn't exist" - ); - return Ok(None); - }; - - let identities = request_state.store.get_identities(device).await?; + let identities = request_state.store.get_identities(state.other_device_data.clone()).await?; let verification = if let Some(identity) = &identities.identity_being_verified { match &identity { @@ -1240,7 +1240,7 @@ async fn generate_qr_code( } else { warn!( user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, + device_id = ?state.other_device_data.device_id(), "Can't create a QR code, the other device \ doesn't have a valid device key" ); @@ -1259,7 +1259,7 @@ async fn generate_qr_code( } else { warn!( user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, + device_id = ?state.other_device_data.device_id(), "Can't create a QR code, our cross signing identity \ doesn't contain a valid master key" ); @@ -1288,7 +1288,7 @@ async fn generate_qr_code( } else { warn!( user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, + device_id = ?state.other_device_data.device_id(), "Can't create a QR code, we don't trust our own \ master key" ); @@ -1297,7 +1297,7 @@ async fn generate_qr_code( } else { warn!( user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, + device_id = ?state.other_device_data.device_id(), "Can't create a QR code, the user's identity \ doesn't have a valid master key" ); @@ -1308,7 +1308,7 @@ async fn generate_qr_code( } else { warn!( user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, + device_id = ?state.other_device_data.device_id(), "Can't create a QR code, the user doesn't have a valid cross \ signing identity." ); @@ -1478,22 +1478,7 @@ async fn start_sas( return Ok(None); } - // TODO signal why starting the sas flow doesn't work? - let Some(device) = request_state - .store - .get_device(&request_state.other_user_id, &state.other_device_id) - .await? - else { - warn!( - user_id = ?request_state.other_user_id, - device_id = ?state.other_device_id, - "Can't start the SAS verification flow, the device that \ - accepted the verification doesn't exist" - ); - return Ok(None); - }; - - let identities = request_state.store.get_identities(device).await?; + let identities = request_state.store.get_identities(state.other_device_data.clone()).await?; let (state, sas, content) = match request_state.flow_id.as_ref() { FlowId::ToDevice(t) => { @@ -1653,7 +1638,10 @@ mod tests { let event_id = event_id!("$1234localhost").to_owned(); let room_id = room_id!("!test:localhost").to_owned(); - let (_alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, bob, bob_store) = setup_stores().await; + + let alice_device_data = DeviceData::from_account(&alice); + let bob_device_data = DeviceData::from_account(&bob); let content = VerificationRequest::request( &bob_store.account.user_id, @@ -1662,12 +1650,6 @@ mod tests { None, ); - let device_data = alice_store - .get_device(&bob_store.account.user_id, &bob_store.account.device_id) - .await - .unwrap() - .expect("Missing device data"); - let flow_id = FlowId::InRoom(room_id, event_id); let bob_request = VerificationRequest::new( @@ -1690,7 +1672,7 @@ mod tests { bob_id(), flow_id, &(&content).into(), - device_data, + bob_device_data, ); assert_matches!(alice_request.state(), VerificationRequestState::Requested { .. }); @@ -1698,7 +1680,7 @@ mod tests { let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap(); let content = ReadyContent::try_from(&content).unwrap(); - bob_request.receive_ready(alice_id(), &content); + bob_request.receive_ready(alice_id(), &content, alice_device_data); assert_matches!(bob_request.state(), VerificationRequestState::Ready { .. }); assert_matches!(alice_request.state(), VerificationRequestState::Ready { .. }); @@ -1748,8 +1730,10 @@ mod tests { let event_id = event_id!("$1234localhost"); let room_id = room_id!("!test:localhost"); - let (_alice, alice_store, bob, bob_store) = setup_stores().await; - let bob_device = DeviceData::from_account(&bob); + let (alice, alice_store, bob, bob_store) = setup_stores().await; + + let alice_device_data = DeviceData::from_account(&alice); + let bob_device_data = DeviceData::from_account(&bob); let content = VerificationRequest::request( &bob_store.account.user_id, @@ -1758,12 +1742,6 @@ mod tests { None, ); - let device_data = alice_store - .get_device(&bob_store.account.user_id, &bob_store.account.device_id) - .await - .unwrap() - .expect("Missing device data"); - let flow_id = FlowId::from((room_id, event_id)); let bob_request = VerificationRequest::new( @@ -1782,19 +1760,19 @@ mod tests { bob_id(), flow_id, &(&content).into(), - device_data, + bob_device_data.clone(), ); - do_accept_request(&alice_request, &bob_request, None); + do_accept_request(&alice_request, alice_device_data, &bob_request, None); let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); let content: OutgoingContent = request.try_into().unwrap(); let content = StartContent::try_from(&content).unwrap(); let flow_id = content.flow_id().to_owned(); - alice_request.receive_start(bob_device.user_id(), &content).await.unwrap(); + alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap(); let alice_sas = - alice_request.verification_cache.get_sas(bob_device.user_id(), &flow_id).unwrap(); + alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap(); assert_matches!( alice_request.state(), @@ -1811,22 +1789,24 @@ mod tests { #[async_test] async fn test_requesting_until_sas_to_device() { - let (_alice, alice_store, bob, bob_store) = setup_stores().await; - let bob_device = DeviceData::from_account(&bob); + let (alice, alice_store, bob, bob_store) = setup_stores().await; + + let alice_device_data = DeviceData::from_account(&alice); + let bob_device_data = DeviceData::from_account(&bob); // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), None); let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; - do_accept_request(&alice_request, &bob_request, None); + do_accept_request(&alice_request, alice_device_data, &bob_request, None); let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); let content: OutgoingContent = request.try_into().unwrap(); let content = StartContent::try_from(&content).unwrap(); let flow_id = content.flow_id().to_owned(); - alice_request.receive_start(bob_device.user_id(), &content).await.unwrap(); + alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap(); let alice_sas = - alice_request.verification_cache.get_sas(bob_device.user_id(), &flow_id).unwrap(); + alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap(); assert_matches!( alice_request.state(), @@ -1846,7 +1826,9 @@ mod tests { #[async_test] #[cfg(feature = "qrcode")] async fn test_can_scan_another_qr_after_creating_mine() { - let (_alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, _bob, bob_store) = setup_stores().await; + + let alice_device_data = DeviceData::from_account(&alice); // Set up the pair of verification requests let bob_request = build_test_request( @@ -1857,6 +1839,7 @@ mod tests { let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; do_accept_request( &alice_request, + alice_device_data, &bob_request, Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]), ); @@ -1897,12 +1880,14 @@ mod tests { #[async_test] #[cfg(feature = "qrcode")] async fn test_can_start_sas_after_generating_qr_code() { - let (_alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, _bob, bob_store) = setup_stores().await; + + let alice_device_data = DeviceData::from_account(&alice); // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods())); let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; - do_accept_request(&alice_request, &bob_request, Some(all_methods())); + do_accept_request(&alice_request, alice_device_data, &bob_request, Some(all_methods())); // Each side can start its own QR verification flow by generating QR code let alice_verification = alice_request.generate_qr_code().await.unwrap(); @@ -1942,12 +1927,14 @@ mod tests { #[async_test] #[cfg(feature = "qrcode")] async fn test_start_sas_after_scan_cancels_request() { - let (_alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, _bob, bob_store) = setup_stores().await; + + let alice_device_data = DeviceData::from_account(&alice); // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods())); let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; - do_accept_request(&alice_request, &bob_request, Some(all_methods())); + do_accept_request(&alice_request, alice_device_data, &bob_request, Some(all_methods())); // Bob generates a QR code let bob_verification = bob_request.generate_qr_code().await.unwrap().unwrap(); @@ -2057,6 +2044,7 @@ mod tests { /// default list of methods will be used. fn do_accept_request( accepting_request: &VerificationRequest, + accepting_device_data: DeviceData, initiating_request: &VerificationRequest, methods: Option>, ) { @@ -2066,7 +2054,11 @@ mod tests { }; let content: OutgoingContent = request.unwrap().try_into().unwrap(); let content = ReadyContent::try_from(&content).unwrap(); - initiating_request.receive_ready(accepting_request.own_user_id(), &content); + initiating_request.receive_ready( + accepting_request.own_user_id(), + &content, + accepting_device_data, + ); assert!(initiating_request.is_ready()); assert!(accepting_request.is_ready()); diff --git a/crates/matrix-sdk/src/encryption/verification/requests.rs b/crates/matrix-sdk/src/encryption/verification/requests.rs index 0e6fb35f510..3892826d2ee 100644 --- a/crates/matrix-sdk/src/encryption/verification/requests.rs +++ b/crates/matrix-sdk/src/encryption/verification/requests.rs @@ -16,7 +16,7 @@ use futures_util::{Stream, StreamExt}; use matrix_sdk_base::crypto::{ CancelInfo, DeviceData, VerificationRequest as BaseVerificationRequest, }; -use ruma::{events::key::verification::VerificationMethod, OwnedDeviceId, RoomId}; +use ruma::{events::key::verification::VerificationMethod, RoomId}; #[cfg(feature = "qrcode")] use super::{QrVerification, QrVerificationData}; @@ -55,9 +55,9 @@ pub enum VerificationRequestState { /// The verification methods supported by the us. our_methods: Vec, - /// The device ID of the device that responded to the verification + /// The device data of the device that responded to the verification /// request. - other_device_id: OwnedDeviceId, + other_device_data: DeviceData, }, /// The verification request has transitioned into a concrete verification /// flow. For example it transitioned into the emoji based SAS @@ -223,8 +223,8 @@ impl VerificationRequest { Requested { their_methods, other_device_data } => { VerificationRequestState::Requested { their_methods, other_device_data } } - Ready { their_methods, our_methods, other_device_id } => { - VerificationRequestState::Ready { their_methods, our_methods, other_device_id } + Ready { their_methods, our_methods, other_device_data } => { + VerificationRequestState::Ready { their_methods, our_methods, other_device_data } } Transitioned { verification } => VerificationRequestState::Transitioned { verification: match verification { From 61b651865a421de380c21fe5183db3261a2561fc Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 24 Oct 2024 17:52:51 +0300 Subject: [PATCH 11/11] Pass the `DeviceData` in between the `Ready` and `Transitioned` states instead of fetching it from the store. --- .../src/verification/requests.rs | 219 ++++++++++++------ .../src/encryption/verification/requests.rs | 2 +- 2 files changed, 152 insertions(+), 69 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/verification/requests.rs b/crates/matrix-sdk-crypto/src/verification/requests.rs index 7b8d7a27338..3815ff49e5c 100644 --- a/crates/matrix-sdk-crypto/src/verification/requests.rs +++ b/crates/matrix-sdk-crypto/src/verification/requests.rs @@ -101,6 +101,10 @@ pub enum VerificationRequestState { /// The concrete [`Verification`] object the verification request /// transitioned into. verification: Verification, + + /// The device data of the device that responded to the verification + /// request. + other_device_data: DeviceData, }, /// The verification flow that was started with this request has finished. Done, @@ -123,9 +127,10 @@ impl From<&InnerRequest> for VerificationRequestState { our_methods: s.state.our_methods.to_owned(), other_device_data: s.state.other_device_data.to_owned(), }, - InnerRequest::Transitioned(s) => { - Self::Transitioned { verification: s.state.verification.to_owned() } - } + InnerRequest::Transitioned(s) => Self::Transitioned { + verification: s.state.verification.to_owned(), + other_device_data: s.state.other_device_data.to_owned(), + }, InnerRequest::Passive(_) => { Self::Cancelled(Cancelled::new(true, CancelCode::Accepted).into()) } @@ -1198,6 +1203,7 @@ async fn scan_qr_code( state: Transitioned { ready: state.to_owned(), verification: verification.to_owned().into(), + other_device_data: state.other_device_data.to_owned(), }, }; @@ -1325,6 +1331,7 @@ async fn generate_qr_code( state: Transitioned { ready: state.to_owned(), verification: verification.to_owned().into(), + other_device_data: state.other_device_data.to_owned(), }, }; @@ -1351,17 +1358,8 @@ async fn receive_start( "Received a new verification start event", ); - let Some(device) = request_state.store.get_device(sender, content.from_device()).await? else { - warn!( - ?sender, - device = ?content.from_device(), - "Received a key verification start event from an unknown device", - ); - - return Ok(None); - }; - - let identities = request_state.store.get_identities(device.clone()).await?; + let other_device_data = state.other_device_data.clone(); + let identities = request_state.store.get_identities(other_device_data.clone()).await?; let own_user_id = &request_state.store.account.user_id; let own_device_id = &request_state.store.account.device_id; @@ -1385,7 +1383,10 @@ async fn receive_start( // we're the lexicographically smaller user ID (or device ID if equal). use std::cmp::Ordering; if !matches!( - (sender.cmp(own_user_id), device.device_id().cmp(own_device_id)), + ( + sender.cmp(own_user_id), + other_device_data.device_id().cmp(own_device_id) + ), (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater) ) { info!("Started a new SAS verification, replacing an already started one."); @@ -1424,14 +1425,14 @@ async fn receive_start( } Err(c) => { warn!( - user_id = ?device.user_id(), - device_id = ?device.device_id(), + user_id = ?other_device_data.user_id(), + device_id = ?other_device_data.device_id(), content = ?c, "Can't start key verification, canceling.", ); request_state.verification_cache.queue_up_content( - device.user_id(), - device.device_id(), + other_device_data.user_id(), + other_device_data.device_id(), c, None, ); @@ -1485,8 +1486,11 @@ async fn start_sas( let (sas, content) = Sas::start(identities, t.to_owned(), we_started, Some(request_handle), None); - let state = - Transitioned { ready: state.to_owned(), verification: sas.to_owned().into() }; + let state = Transitioned { + ready: state.to_owned(), + verification: sas.to_owned().into(), + other_device_data: state.other_device_data.to_owned(), + }; (state, sas, content) } @@ -1498,8 +1502,11 @@ async fn start_sas( we_started, request_handle, ); - let state = - Transitioned { ready: state.to_owned(), verification: sas.to_owned().into() }; + let state = Transitioned { + ready: state.to_owned(), + verification: sas.to_owned().into(), + other_device_data: state.other_device_data.to_owned(), + }; (state, sas, content) } }; @@ -1555,7 +1562,11 @@ impl Ready { store: request_state.store.to_owned(), flow_id: request_state.flow_id.to_owned(), other_user_id: request_state.other_user_id.to_owned(), - state: Transitioned { ready: self.clone(), verification }, + state: Transitioned { + ready: self.clone(), + verification, + other_device_data: self.other_device_data.clone(), + }, } } } @@ -1564,6 +1575,7 @@ impl Ready { struct Transitioned { ready: Ready, verification: Verification, + other_device_data: DeviceData, } impl RequestState { @@ -1763,7 +1775,7 @@ mod tests { bob_device_data.clone(), ); - do_accept_request(&alice_request, alice_device_data, &bob_request, None); + do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None); let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); @@ -1774,15 +1786,24 @@ mod tests { let alice_sas = alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap(); - assert_matches!( - alice_request.state(), - VerificationRequestState::Transitioned { verification: Verification::SasV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::SasV1(_), + other_device_data + } = alice_request.state() ); - assert_matches!( - bob_request.state(), - VerificationRequestState::Transitioned { verification: Verification::SasV1(_) } + + assert_eq!(bob_device_data, other_device_data); + + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::SasV1(_), + other_device_data + } = bob_request.state() ); + assert_eq!(alice_device_data, other_device_data); + assert!(!bob_sas.is_cancelled()); assert!(!alice_sas.is_cancelled()); } @@ -1797,7 +1818,7 @@ mod tests { // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), None); let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; - do_accept_request(&alice_request, alice_device_data, &bob_request, None); + do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None); let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); @@ -1808,15 +1829,24 @@ mod tests { let alice_sas = alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap(); - assert_matches!( - alice_request.state(), - VerificationRequestState::Transitioned { verification: Verification::SasV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::SasV1(_), + other_device_data + } = alice_request.state() ); - assert_matches!( - bob_request.state(), - VerificationRequestState::Transitioned { verification: Verification::SasV1(_) } + + assert_eq!(bob_device_data, other_device_data); + + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::SasV1(_), + other_device_data + } = bob_request.state() ); + assert_eq!(alice_device_data, other_device_data); + assert!(!bob_sas.is_cancelled()); assert!(!alice_sas.is_cancelled()); assert!(alice_sas.started_from_request()); @@ -1826,9 +1856,10 @@ mod tests { #[async_test] #[cfg(feature = "qrcode")] async fn test_can_scan_another_qr_after_creating_mine() { - let (alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, bob, bob_store) = setup_stores().await; let alice_device_data = DeviceData::from_account(&alice); + let bob_device_data = DeviceData::from_account(&bob); // Set up the pair of verification requests let bob_request = build_test_request( @@ -1839,7 +1870,7 @@ mod tests { let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; do_accept_request( &alice_request, - alice_device_data, + alice_device_data.clone(), &bob_request, Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]), ); @@ -1848,15 +1879,24 @@ mod tests { let alice_verification = alice_request.generate_qr_code().await.unwrap(); let bob_verification = bob_request.generate_qr_code().await.unwrap(); - assert_matches!( - alice_request.state(), - VerificationRequestState::Transitioned { verification: Verification::QrV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::QrV1(_), + other_device_data + } = alice_request.state() ); - assert_matches!( - bob_request.state(), - VerificationRequestState::Transitioned { verification: Verification::QrV1(_) } + + assert_eq!(bob_device_data, other_device_data); + + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::QrV1(_), + other_device_data + } = bob_request.state() ); + assert_eq!(alice_device_data, other_device_data); + assert!(alice_verification.is_some()); assert!(bob_verification.is_some()); @@ -1867,10 +1907,13 @@ mod tests { assert_let!( VerificationRequestState::Transitioned { - verification: Verification::QrV1(alice_verification) + verification: Verification::QrV1(alice_verification), + other_device_data } = alice_request.state() ); + assert_eq!(bob_device_data, other_device_data); + // Finally we assert that the verification has been reciprocated rather than // cancelled due to a duplicate verification flow assert!(!alice_verification.is_cancelled()); @@ -1880,34 +1923,48 @@ mod tests { #[async_test] #[cfg(feature = "qrcode")] async fn test_can_start_sas_after_generating_qr_code() { - let (alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, bob, bob_store) = setup_stores().await; let alice_device_data = DeviceData::from_account(&alice); + let bob_device_data = DeviceData::from_account(&bob); // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods())); let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; - do_accept_request(&alice_request, alice_device_data, &bob_request, Some(all_methods())); + do_accept_request( + &alice_request, + alice_device_data.clone(), + &bob_request, + Some(all_methods()), + ); // Each side can start its own QR verification flow by generating QR code let alice_verification = alice_request.generate_qr_code().await.unwrap(); let bob_verification = bob_request.generate_qr_code().await.unwrap(); - assert_matches!( - alice_request.state(), - VerificationRequestState::Transitioned { verification: Verification::QrV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::QrV1(_), + other_device_data + } = alice_request.state() ); + assert_eq!(bob_device_data, other_device_data); + assert!(alice_verification.is_some()); assert!(bob_verification.is_some()); // Alice can now start SAS verification flow instead of QR without cancelling // the request let (sas, request) = alice_request.start_sas().await.unwrap().unwrap(); - assert_matches!( - alice_request.state(), - VerificationRequestState::Transitioned { verification: Verification::SasV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::SasV1(_), + other_device_data + } = alice_request.state() ); + + assert_eq!(bob_device_data, other_device_data); assert!(!sas.is_cancelled()); // Bob receives the SAS start @@ -1917,9 +1974,14 @@ mod tests { // Bob should now have transitioned to SAS... assert_let!( - VerificationRequestState::Transitioned { verification: Verification::SasV1(bob_sas) } = - bob_request.state() + VerificationRequestState::Transitioned { + verification: Verification::SasV1(bob_sas), + other_device_data + } = bob_request.state() ); + + assert_eq!(alice_device_data, other_device_data); + // ... and, more to the point, it should not be cancelled. assert!(!bob_sas.is_cancelled()); } @@ -1927,40 +1989,58 @@ mod tests { #[async_test] #[cfg(feature = "qrcode")] async fn test_start_sas_after_scan_cancels_request() { - let (alice, alice_store, _bob, bob_store) = setup_stores().await; + let (alice, alice_store, bob, bob_store) = setup_stores().await; let alice_device_data = DeviceData::from_account(&alice); + let bob_device_data = DeviceData::from_account(&bob); // Set up the pair of verification requests let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods())); let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await; - do_accept_request(&alice_request, alice_device_data, &bob_request, Some(all_methods())); + do_accept_request( + &alice_request, + alice_device_data.clone(), + &bob_request, + Some(all_methods()), + ); // Bob generates a QR code let bob_verification = bob_request.generate_qr_code().await.unwrap().unwrap(); - assert_matches!( - bob_request.state(), - VerificationRequestState::Transitioned { verification: Verification::QrV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::QrV1(_), + other_device_data + } = bob_request.state() ); + assert_eq!(alice_device_data, other_device_data); + // Now Alice scans Bob's code let bob_qr_code = bob_verification.to_bytes().unwrap(); let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap(); let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap(); assert_let!( - VerificationRequestState::Transitioned { verification: Verification::QrV1(alice_qr) } = - alice_request.state() + VerificationRequestState::Transitioned { + verification: Verification::QrV1(alice_qr), + other_device_data + } = alice_request.state() ); + + assert_eq!(bob_device_data, other_device_data); assert!(alice_qr.reciprocated()); // But Bob wants to do an SAS verification! let (_, request) = bob_request.start_sas().await.unwrap().unwrap(); - assert_matches!( - bob_request.state(), - VerificationRequestState::Transitioned { verification: Verification::SasV1(_) } + assert_let!( + VerificationRequestState::Transitioned { + verification: Verification::SasV1(_), + other_device_data + } = bob_request.state() ); + assert_eq!(alice_device_data, other_device_data); + // Alice receives the SAS start let content: OutgoingContent = request.try_into().unwrap(); let content = StartContent::try_from(&content).unwrap(); @@ -1972,9 +2052,12 @@ mod tests { // and she should now have a *cancelled* SAS verification assert_let!( VerificationRequestState::Transitioned { - verification: Verification::SasV1(alice_sas) + verification: Verification::SasV1(alice_sas), + other_device_data } = alice_request.state() ); + + assert_eq!(bob_device_data, other_device_data); assert!(alice_sas.is_cancelled()); } diff --git a/crates/matrix-sdk/src/encryption/verification/requests.rs b/crates/matrix-sdk/src/encryption/verification/requests.rs index 3892826d2ee..49b8a51b438 100644 --- a/crates/matrix-sdk/src/encryption/verification/requests.rs +++ b/crates/matrix-sdk/src/encryption/verification/requests.rs @@ -226,7 +226,7 @@ impl VerificationRequest { Ready { their_methods, our_methods, other_device_data } => { VerificationRequestState::Ready { their_methods, our_methods, other_device_data } } - Transitioned { verification } => VerificationRequestState::Transitioned { + Transitioned { verification, .. } => VerificationRequestState::Transitioned { verification: match verification { matrix_sdk_base::crypto::Verification::SasV1(s) => { Verification::SasV1(SasVerification { inner: s, client })