From 09a7858702b0dfc72edb8c0df5c5b1b1e5c24575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 13 May 2021 11:15:56 +0200 Subject: [PATCH] crypto: Initial support for the longer to-device verification flow --- matrix_sdk/src/client.rs | 25 +- matrix_sdk/src/lib.rs | 3 + matrix_sdk/src/verification_request.rs | 27 +- matrix_sdk_crypto/src/machine.rs | 22 +- matrix_sdk_crypto/src/verification/machine.rs | 107 ++-- matrix_sdk_crypto/src/verification/mod.rs | 37 ++ .../src/verification/requests.rs | 468 ++++++++++++------ .../src/verification/sas/event_enums.rs | 70 ++- .../src/verification/sas/helpers.rs | 10 +- .../src/verification/sas/inner_sas.rs | 9 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 18 +- .../src/verification/sas/sas_state.rs | 32 +- 12 files changed, 558 insertions(+), 270 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index cacd998e..910e9ae2 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -103,14 +103,11 @@ use matrix_sdk_common::{ }; #[cfg(feature = "encryption")] -use matrix_sdk_common::{ - api::r0::{ - keys::{get_keys, upload_keys, upload_signing_keys::Request as UploadSigningKeysRequest}, - to_device::send_event_to_device::{ - Request as RumaToDeviceRequest, Response as ToDeviceResponse, - }, +use matrix_sdk_common::api::r0::{ + keys::{get_keys, upload_keys, upload_signing_keys::Request as UploadSigningKeysRequest}, + to_device::send_event_to_device::{ + Request as RumaToDeviceRequest, Response as ToDeviceResponse, }, - identifiers::EventId, }; use matrix_sdk_common::locks::Mutex; @@ -2131,13 +2128,17 @@ impl Client { /// Get a `VerificationRequest` object with the given flow id. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] - pub async fn get_verification_request(&self, flow_id: &EventId) -> Option { + pub async fn get_verification_request( + &self, + flow_id: impl AsRef, + ) -> Option { let olm = self.base_client.olm_machine().await?; - olm.get_verification_request(flow_id).and_then(|r| { - self.get_joined_room(r.room_id()) - .map(|room| VerificationRequest { inner: r, room }) - }) + olm.get_verification_request(flow_id) + .map(|r| VerificationRequest { + inner: r, + client: self.clone(), + }) } /// Get a specific device of a user. diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 4ebc7ef1..8f346874 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -110,6 +110,9 @@ pub use room_member::RoomMember; #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub use sas::Sas; +#[cfg(feature = "encryption")] +#[cfg_attr(feature = "docs", doc(cfg(encryption)))] +pub use verification_request::VerificationRequest; #[cfg(not(target_arch = "wasm32"))] pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/matrix_sdk/src/verification_request.rs b/matrix_sdk/src/verification_request.rs index 13b405e8..88688eeb 100644 --- a/matrix_sdk/src/verification_request.rs +++ b/matrix_sdk/src/verification_request.rs @@ -12,27 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -use matrix_sdk_base::{ - crypto::VerificationRequest as BaseVerificationRequest, events::AnyMessageEventContent, +use matrix_sdk_base::crypto::{ + OutgoingVerificationRequest, VerificationRequest as BaseVerificationRequest, }; -use crate::{room::Joined, Result}; +use crate::{Client, Result}; /// An object controling the interactive verification flow. #[derive(Debug, Clone)] pub struct VerificationRequest { pub(crate) inner: BaseVerificationRequest, - pub(crate) room: Joined, + pub(crate) client: Client, } impl VerificationRequest { - /// Accept the interactive verification flow. + /// Accept the verification request pub async fn accept(&self) -> Result<()> { - if let Some(content) = self.inner.accept() { - let content = AnyMessageEventContent::KeyVerificationReady(content); - self.room.send(content, None).await?; + if let Some(request) = self.inner.accept() { + match request { + OutgoingVerificationRequest::ToDevice(r) => { + self.client.send_to_device(&r).await?; + } + OutgoingVerificationRequest::InRoom(r) => { + self.client.room_send_helper(&r).await?; + } + }; } Ok(()) } + + /// Cancel the verification request + pub async fn cancel(&self) -> Result<()> { + todo!() + } } diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index acc00784..31133fff 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -37,8 +37,7 @@ use matrix_sdk_common::{ AnyMessageEventContent, AnyToDeviceEvent, SyncMessageEvent, ToDeviceEvent, }, identifiers::{ - DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, EventId, RoomId, - UserId, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, UserId, }, locks::Mutex, uuid::Uuid, @@ -317,8 +316,7 @@ impl OlmMachine { requests.push(request); } - requests.append(&mut self.outgoing_to_device_requests()); - requests.append(&mut self.verification_machine.outgoing_room_message_requests()); + requests.append(&mut self.verification_machine.outgoing_messages()); requests.append( &mut self .key_request_machine @@ -747,11 +745,6 @@ impl OlmMachine { } } - /// Get the to-device requests that need to be sent out. - fn outgoing_to_device_requests(&self) -> Vec { - self.verification_machine.outgoing_to_device_requests() - } - /// Mark an outgoing to-device requests as sent. async fn mark_to_device_request_as_sent(&self, request_id: &Uuid) -> StoreResult<()> { self.verification_machine.mark_request_as_sent(request_id); @@ -773,7 +766,10 @@ impl OlmMachine { } /// Get a verification request object with the given flow id. - pub fn get_verification_request(&self, flow_id: &EventId) -> Option { + pub fn get_verification_request( + &self, + flow_id: impl AsRef, + ) -> Option { self.verification_machine.get_request(flow_id) } @@ -1967,14 +1963,16 @@ pub(crate) mod test { alice.handle_verification_event(&event).await; let event = alice - .outgoing_to_device_requests() + .verification_machine + .outgoing_messages() .first() .map(|r| outgoing_request_to_event(alice.user_id(), r)) .unwrap(); bob.handle_verification_event(&event).await; let event = bob - .outgoing_to_device_requests() + .verification_machine + .outgoing_messages() .first() .map(|r| outgoing_request_to_event(bob.user_id(), r)) .unwrap(); diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 5934ef15..123ffe1a 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -47,9 +47,8 @@ pub struct VerificationMachine { pub(crate) store: Arc>, verifications: Arc>, room_verifications: Arc>, - requests: Arc>, - outgoing_to_device_messages: Arc>, - outgoing_room_messages: Arc>, + requests: Arc>, + outgoing_messages: Arc>, } impl VerificationMachine { @@ -64,9 +63,8 @@ impl VerificationMachine { store, verifications: DashMap::new().into(), requests: DashMap::new().into(), - outgoing_to_device_messages: DashMap::new().into(), room_verifications: DashMap::new().into(), - outgoing_room_messages: DashMap::new().into(), + outgoing_messages: DashMap::new().into(), } } @@ -83,6 +81,7 @@ impl VerificationMachine { device.clone(), self.store.clone(), identity, + None, ); let request = match content.into() { @@ -93,7 +92,8 @@ impl VerificationMachine { } .into(), OutgoingContent::ToDevice(c) => { - let request = content_to_request(device.user_id(), device.device_id(), c); + let request = + content_to_request(device.user_id(), device.device_id().to_owned(), c); self.verifications .insert(sas.flow_id().as_str().to_owned(), sas.clone()); @@ -105,9 +105,8 @@ impl VerificationMachine { Ok((sas, request)) } - pub fn get_request(&self, flow_id: &EventId) -> Option { - #[allow(clippy::map_clone)] - self.requests.get(flow_id).map(|s| s.clone()) + pub fn get_request(&self, flow_id: impl AsRef) -> Option { + self.requests.get(flow_id.as_ref()).map(|s| s.clone()) } pub fn get_sas(&self, transaction_id: &str) -> Option { @@ -134,7 +133,7 @@ impl VerificationMachine { ) { match content { OutgoingContent::ToDevice(c) => { - let request = content_to_request(recipient, recipient_device, c); + let request = content_to_request(recipient, recipient_device.to_owned(), c); let request_id = request.txn_id; let request = OutgoingRequest { @@ -142,7 +141,7 @@ impl VerificationMachine { request: Arc::new(request.into()), }; - self.outgoing_to_device_messages.insert(request_id, request); + self.outgoing_messages.insert(request_id, request); } OutgoingContent::Room(r, c) => { @@ -160,12 +159,11 @@ impl VerificationMachine { request_id, }; - self.outgoing_room_messages.insert(request_id, request); + self.outgoing_messages.insert(request_id, request); } } } - #[allow(dead_code)] fn receive_room_event_helper(&self, sas: &Sas, event: &AnyMessageEvent) { if let Some(c) = sas.receive_room_event(event) { self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c); @@ -179,20 +177,11 @@ impl VerificationMachine { } pub fn mark_request_as_sent(&self, uuid: &Uuid) { - self.outgoing_room_messages.remove(uuid); - self.outgoing_to_device_messages.remove(uuid); + self.outgoing_messages.remove(uuid); } - pub fn outgoing_room_message_requests(&self) -> Vec { - self.outgoing_room_messages - .iter() - .map(|r| (*r).clone()) - .collect() - } - - pub fn outgoing_to_device_requests(&self) -> Vec { - #[allow(clippy::map_clone)] - self.outgoing_to_device_messages + pub fn outgoing_messages(&self) -> Vec { + self.outgoing_messages .iter() .map(|r| (*r).clone()) .collect() @@ -204,7 +193,7 @@ impl VerificationMachine { for sas in self.verifications.iter() { if let Some(r) = sas.cancel_if_timed_out() { - self.outgoing_to_device_messages.insert( + self.outgoing_messages.insert( r.request_id(), OutgoingRequest { request_id: r.request_id(), @@ -240,22 +229,23 @@ impl VerificationMachine { m.sender, r.from_device ); - let request = VerificationRequest::from_request_event( + let request = VerificationRequest::from_room_request( self.account.clone(), self.private_identity.lock().await.clone(), self.store.clone(), - room_id, &m.sender, &m.event_id, + room_id, r, ); - self.requests.insert(m.event_id.clone(), request); + self.requests + .insert(request.flow_id().as_str().to_owned(), request); } } } AnySyncMessageEvent::KeyVerificationReady(e) => { - if let Some(request) = self.requests.get(&e.content.relation.event_id) { + if let Some(request) = self.requests.get(e.content.relation.event_id.as_str()) { if &e.sender == request.other_user() { // TODO remove this unwrap. request.receive_ready(&e.sender, &e.content).unwrap(); @@ -268,7 +258,9 @@ impl VerificationMachine { e.sender, e.content.from_device ); - if let Some((_, request)) = self.requests.remove(&e.content.relation.event_id) { + if let Some((_, request)) = + self.requests.remove(e.content.relation.event_id.as_str()) + { if let Some(d) = self .store .get_device(&e.sender, &e.content.from_device) @@ -282,7 +274,7 @@ impl VerificationMachine { Ok(s) => { info!( "Started a new SAS verification, \ - automatically accepting because of in-room" + automatically accepting because we accepted from a request" ); // TODO remove this unwrap @@ -291,7 +283,7 @@ impl VerificationMachine { self.room_verifications .insert(e.content.relation.event_id.clone(), s); - self.outgoing_room_messages + self.outgoing_messages .insert(accept_request.request_id(), accept_request.into()); } Err(c) => { @@ -299,7 +291,7 @@ impl VerificationMachine { "Can't start key verification with {} {}, canceling: {:?}", e.sender, e.content.from_device, c ); - // self.queue_up_content(&e.sender, &e.content.from_device, c) + self.queue_up_content(&e.sender, &e.content.from_device, c) } } } @@ -339,14 +331,12 @@ impl VerificationMachine { } } VerificationResult::Cancel(r) => { - self.outgoing_to_device_messages - .insert(r.request_id(), r.into()); + self.outgoing_messages.insert(r.request_id(), r.into()); } VerificationResult::SignatureUpload(r) => { let request: OutgoingRequest = r.into(); - self.outgoing_to_device_messages - .insert(request.request_id, request); + self.outgoing_messages.insert(request.request_id, request); if let Some(c) = content { self.queue_up_content( @@ -371,6 +361,26 @@ impl VerificationMachine { trace!("Received a key verification event {:?}", event); match event { + AnyToDeviceEvent::KeyVerificationRequest(e) => { + let request = VerificationRequest::from_request( + self.account.clone(), + self.private_identity.lock().await.clone(), + self.store.clone(), + &e.sender, + &e.content, + ); + + self.requests + .insert(request.flow_id().as_str().to_string(), request); + } + AnyToDeviceEvent::KeyVerificationReady(e) => { + if let Some(request) = self.requests.get(&e.content.transaction_id) { + if &e.sender == request.other_user() { + // TODO remove this unwrap. + request.receive_ready(&e.sender, &e.content).unwrap(); + } + } + } AnyToDeviceEvent::KeyVerificationStart(e) => { trace!( "Received a m.key.verification start event from {} {}", @@ -432,7 +442,7 @@ impl VerificationMachine { match s.mark_as_done().await? { VerificationResult::Ok => (), VerificationResult::Cancel(r) => { - self.outgoing_to_device_messages.insert( + self.outgoing_messages.insert( r.request_id(), OutgoingRequest { request_id: r.request_id(), @@ -443,7 +453,7 @@ impl VerificationMachine { VerificationResult::SignatureUpload(r) => { let request_id = Uuid::new_v4(); - self.outgoing_to_device_messages.insert( + self.outgoing_messages.insert( request_id, OutgoingRequest { request_id, @@ -521,6 +531,7 @@ mod test { alice_device, bob_store, None, + None, ); machine @@ -558,15 +569,11 @@ mod test { .map(|c| wrap_any_to_device_content(bob.user_id(), c)) .unwrap(); - assert!(alice_machine.outgoing_to_device_messages.is_empty()); + assert!(alice_machine.outgoing_messages.is_empty()); alice_machine.receive_event(&event).await.unwrap(); - assert!(!alice_machine.outgoing_to_device_messages.is_empty()); + assert!(!alice_machine.outgoing_messages.is_empty()); - let request = alice_machine - .outgoing_to_device_messages - .iter() - .next() - .unwrap(); + let request = alice_machine.outgoing_messages.iter().next().unwrap(); let txn_id = *request.request_id(); @@ -611,14 +618,14 @@ mod test { let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); assert!(!alice.timed_out()); - assert!(alice_machine.outgoing_to_device_messages.is_empty()); + assert!(alice_machine.outgoing_messages.is_empty()); // This line panics on macOS, so we're disabled for now. alice.set_creation_time(Instant::now() - Duration::from_secs(60 * 15)); assert!(alice.timed_out()); - assert!(alice_machine.outgoing_to_device_messages.is_empty()); + assert!(alice_machine.outgoing_messages.is_empty()); alice_machine.garbage_collect(); - assert!(!alice_machine.outgoing_to_device_messages.is_empty()); + assert!(!alice_machine.outgoing_messages.is_empty()); alice_machine.garbage_collect(); assert!(alice_machine.verifications.is_empty()); } diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index c642dd21..c8a603b7 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -20,6 +20,43 @@ pub use machine::VerificationMachine; pub use requests::VerificationRequest; pub use sas::{AcceptSettings, Sas, VerificationResult}; +use matrix_sdk_common::identifiers::{EventId, RoomId}; + +#[derive(Clone, Debug, Hash, PartialEq, PartialOrd)] +pub enum FlowId { + ToDevice(String), + InRoom(RoomId, EventId), +} + +impl FlowId { + pub fn room_id(&self) -> Option<&RoomId> { + if let FlowId::InRoom(r, _) = &self { + Some(r) + } else { + None + } + } + + pub fn as_str(&self) -> &str { + match self { + FlowId::InRoom(_, r) => r.as_str(), + FlowId::ToDevice(t) => t.as_str(), + } + } +} + +impl From for FlowId { + fn from(transaciton_id: String) -> Self { + FlowId::ToDevice(transaciton_id) + } +} + +impl From<(RoomId, EventId)> for FlowId { + fn from(ids: (RoomId, EventId)) -> Self { + FlowId::InRoom(ids.0, ids.1) + } +} + #[cfg(test)] pub(crate) mod test { use crate::{ diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 18abfac4..3bfc4ba7 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -14,30 +14,130 @@ #![allow(dead_code)] -use std::sync::{Arc, Mutex}; +use std::{ + convert::TryFrom, + sync::{Arc, Mutex}, +}; use matrix_sdk_common::{ - api::r0::message::send_message_event::Response as RoomMessageResponse, + api::r0::to_device::DeviceIdOrAllDevices, events::{ key::verification::{ - ready::ReadyEventContent, start::StartEventContent, Relation, VerificationMethod, + ready::{ReadyEventContent, ReadyToDeviceEventContent}, + request::RequestToDeviceEventContent, + start::StartEventContent, + Relation, VerificationMethod, }, room::message::KeyVerificationRequestEventContent, - MessageEvent, SyncMessageEvent, + AnyMessageEventContent, AnyToDeviceEventContent, MessageEvent, SyncMessageEvent, }, identifiers::{DeviceId, DeviceIdBox, EventId, RoomId, UserId}, + uuid::Uuid, }; use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, store::CryptoStore, - ReadOnlyDevice, Sas, UserIdentities, + OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, Sas, ToDeviceRequest, + UserIdentities, }; -use super::sas::{OutgoingContent, StartContent}; +use super::{ + sas::{content_to_request, OutgoingContent, StartContent}, + FlowId, +}; const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; +pub enum RequestContent<'a> { + ToDevice(&'a RequestToDeviceEventContent), + Room(&'a KeyVerificationRequestEventContent), +} + +impl RequestContent<'_> { + fn from_device(&self) -> &DeviceId { + match self { + RequestContent::ToDevice(t) => &t.from_device, + RequestContent::Room(r) => &r.from_device, + } + } + + fn methods(&self) -> &[VerificationMethod] { + match self { + RequestContent::ToDevice(t) => &t.methods, + RequestContent::Room(r) => &r.methods, + } + } +} + +impl<'a> From<&'a KeyVerificationRequestEventContent> for RequestContent<'a> { + fn from(c: &'a KeyVerificationRequestEventContent) -> Self { + Self::Room(c) + } +} + +impl<'a> From<&'a RequestToDeviceEventContent> for RequestContent<'a> { + fn from(c: &'a RequestToDeviceEventContent) -> Self { + Self::ToDevice(c) + } +} + +pub enum ReadyContent<'a> { + ToDevice(&'a ReadyToDeviceEventContent), + Room(&'a ReadyEventContent), +} + +impl ReadyContent<'_> { + fn from_device(&self) -> &DeviceId { + match self { + ReadyContent::ToDevice(t) => &t.from_device, + ReadyContent::Room(r) => &r.from_device, + } + } + + fn methods(&self) -> &[VerificationMethod] { + match self { + ReadyContent::ToDevice(t) => &t.methods, + ReadyContent::Room(r) => &r.methods, + } + } +} + +impl<'a> From<&'a ReadyEventContent> for ReadyContent<'a> { + fn from(c: &'a ReadyEventContent) -> Self { + Self::Room(c) + } +} + +impl<'a> From<&'a ReadyToDeviceEventContent> for ReadyContent<'a> { + fn from(c: &'a ReadyToDeviceEventContent) -> Self { + Self::ToDevice(c) + } +} + +impl<'a> TryFrom<&'a OutgoingContent> for ReadyContent<'a> { + type Error = (); + + fn try_from(value: &'a OutgoingContent) -> Result { + match value { + OutgoingContent::Room(_, c) => { + if let AnyMessageEventContent::KeyVerificationReady(c) = c { + Ok(ReadyContent::Room(c)) + } else { + Err(()) + } + } + OutgoingContent::ToDevice(c) => { + if let AnyToDeviceEventContent::KeyVerificationReady(c) = c { + Ok(ReadyContent::ToDevice(c)) + } else { + Err(()) + } + } + } + } +} + #[derive(Clone, Debug)] /// TODO pub struct VerificationRequest { @@ -46,7 +146,7 @@ pub struct VerificationRequest { other_user_id: Arc, private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc>, - room_id: Arc, + flow_id: Arc, } impl VerificationRequest { @@ -55,30 +155,46 @@ impl VerificationRequest { account: ReadOnlyAccount, private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc>, - room_id: Arc, + room_id: &RoomId, + event_id: &EventId, other_user: &UserId, ) -> Self { + let flow_id = (room_id.to_owned(), event_id.to_owned()).into(); + let inner = Mutex::new(InnerRequest::Created(RequestState::new( account.user_id(), account.device_id(), other_user, + &flow_id, ))) .into(); + Self { inner, account, private_cross_signing_identity, store, other_user_id: other_user.clone().into(), - room_id, + flow_id: flow_id.into(), } } /// TODO - pub fn request(&self) -> Option { - match &*self.inner.lock().unwrap() { - InnerRequest::Created(c) => Some(c.as_content()), - _ => None, + pub fn request( + own_user_id: &UserId, + own_device_id: &DeviceId, + other_user_id: &UserId, + ) -> KeyVerificationRequestEventContent { + KeyVerificationRequestEventContent { + body: format!( + "{} is requesting to verify your key, but your client does not \ + support in-chat key verification. You will need to use legacy \ + key verification to verify keys.", + own_user_id + ), + methods: SUPPORTED_METHODS.to_vec(), + from_device: own_device_id.into(), + to: other_user_id.to_owned(), } } @@ -88,23 +204,56 @@ impl VerificationRequest { &self.other_user_id } - /// Mark the request as sent. - pub fn mark_as_sent(&self, response: &RoomMessageResponse) { - let mut inner = self.inner.lock().unwrap(); - - if let InnerRequest::Created(c) = &*inner { - *inner = InnerRequest::Sent(c.clone().into_sent(response)); - } + /// Get the unique ID of this verification request + pub fn flow_id(&self) -> &FlowId { + &self.flow_id } - pub(crate) fn from_request_event( + pub(crate) fn from_room_request( account: ReadOnlyAccount, private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc>, - room_id: &RoomId, sender: &UserId, event_id: &EventId, + room_id: &RoomId, content: &KeyVerificationRequestEventContent, + ) -> Self { + let flow_id = FlowId::from((room_id.to_owned(), event_id.to_owned())); + Self::from_helper( + account, + private_cross_signing_identity, + store, + sender, + flow_id, + content.into(), + ) + } + + pub(crate) fn from_request( + account: ReadOnlyAccount, + private_cross_signing_identity: PrivateCrossSigningIdentity, + store: Arc>, + sender: &UserId, + content: &RequestToDeviceEventContent, + ) -> Self { + let flow_id = FlowId::from(content.transaction_id.to_owned()); + Self::from_helper( + account, + private_cross_signing_identity, + store, + sender, + flow_id, + content.into(), + ) + } + + fn from_helper( + account: ReadOnlyAccount, + private_cross_signing_identity: PrivateCrossSigningIdentity, + store: Arc>, + sender: &UserId, + flow_id: FlowId, + content: RequestContent, ) -> Self { Self { inner: Arc::new(Mutex::new(InnerRequest::Requested( @@ -112,7 +261,7 @@ impl VerificationRequest { account.user_id(), account.device_id(), sender, - event_id, + &flow_id, content, ), ))), @@ -120,29 +269,37 @@ impl VerificationRequest { other_user_id: sender.clone().into(), private_cross_signing_identity, store, - room_id: room_id.clone().into(), + flow_id: flow_id.into(), } } - /// The room id where the verification is happening. - pub fn room_id(&self) -> &RoomId { - &self.room_id - } - /// Accept the verification request. - pub fn accept(&self) -> Option { - self.inner.lock().unwrap().accept() + pub fn accept(&self) -> Option { + let mut inner = self.inner.lock().unwrap(); + + inner.accept().map(|c| match c { + OutgoingContent::ToDevice(content) => self + .content_to_request(inner.other_device_id(), content) + .into(), + OutgoingContent::Room(room_id, content) => RoomMessageRequest { + room_id, + txn_id: Uuid::new_v4(), + content, + } + .into(), + }) } #[allow(clippy::unnecessary_wraps)] - pub(crate) fn receive_ready( + pub(crate) fn receive_ready<'a>( &self, sender: &UserId, - content: &ReadyEventContent, + content: impl Into>, ) -> Result<(), ()> { let mut inner = self.inner.lock().unwrap(); + let content = content.into(); - if let InnerRequest::Sent(s) = &*inner { + if let InnerRequest::Created(s) = &*inner { *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); } @@ -161,14 +318,17 @@ impl VerificationRequest { user_identity: Option, ) -> Result { match &*self.inner.lock().unwrap() { - InnerRequest::Ready(s) => s.clone().into_started_sas( - &event.clone().into_full_event(self.room_id().clone()), - self.store.clone(), - self.account.clone(), - self.private_cross_signing_identity.clone(), - device, - user_identity, - ), + InnerRequest::Ready(s) => match &s.state.flow_id { + FlowId::ToDevice(_) => todo!(), + FlowId::InRoom(r, _) => s.clone().into_started_sas( + &event.clone().into_full_event(r.to_owned()), + self.store.clone(), + self.account.clone(), + self.private_cross_signing_identity.clone(), + device, + user_identity, + ), + }, // TODO cancel here since we got a missmatched message or do // nothing? _ => todo!(), @@ -181,30 +341,50 @@ impl VerificationRequest { user_identity: Option, ) -> Option<(Sas, StartContent)> { match &*self.inner.lock().unwrap() { - InnerRequest::Ready(s) => Some(s.clone().start_sas( - self.room_id(), - self.store.clone(), - self.account.clone(), - self.private_cross_signing_identity.clone(), - device, - user_identity, - )), + InnerRequest::Ready(s) => match &s.state.flow_id { + FlowId::ToDevice(_) => todo!(), + FlowId::InRoom(_, _) => Some(s.clone().start_sas( + self.store.clone(), + self.account.clone(), + self.private_cross_signing_identity.clone(), + device, + user_identity, + )), + }, _ => None, } } + + fn content_to_request( + &self, + other_device_id: DeviceIdOrAllDevices, + content: AnyToDeviceEventContent, + ) -> ToDeviceRequest { + content_to_request(&self.other_user_id, other_device_id, content) + } } #[derive(Debug)] enum InnerRequest { Created(RequestState), - Sent(RequestState), Requested(RequestState), Ready(RequestState), Passive(RequestState), } impl InnerRequest { - fn accept(&mut self) -> Option { + fn other_device_id(&self) -> DeviceIdOrAllDevices { + match self { + InnerRequest::Created(_) => DeviceIdOrAllDevices::AllDevices, + InnerRequest::Requested(_) => DeviceIdOrAllDevices::AllDevices, + InnerRequest::Ready(r) => { + DeviceIdOrAllDevices::DeviceId(r.state.other_device_id.to_owned()) + } + InnerRequest::Passive(_) => DeviceIdOrAllDevices::AllDevices, + } + } + + fn accept(&mut self) -> Option { if let InnerRequest::Requested(s) = self { let (state, content) = s.clone().accept(); *self = InnerRequest::Ready(state); @@ -254,72 +434,49 @@ struct RequestState { state: S, } -#[derive(Clone, Debug)] -struct Created {} - impl RequestState { - fn new(own_user_id: &UserId, own_device_id: &DeviceId, other_user: &UserId) -> Self { + fn new( + own_user_id: &UserId, + own_device_id: &DeviceId, + other_user_id: &UserId, + flow_id: &FlowId, + ) -> Self { Self { - own_user_id: own_user_id.clone(), - own_device_id: own_device_id.into(), - other_user_id: other_user.clone(), - state: Created {}, - } - } - - fn as_content(&self) -> KeyVerificationRequestEventContent { - KeyVerificationRequestEventContent { - body: format!( - "{} is requesting to verify your key, but your client does not \ - support in-chat key verification. You will need to use legacy \ - key verification to verify keys.", - self.own_user_id - ), - methods: SUPPORTED_METHODS.to_vec(), - from_device: self.own_device_id.clone(), - to: self.other_user_id.clone(), - } - } - - fn into_sent(self, response: &RoomMessageResponse) -> RequestState { - RequestState { - own_user_id: self.own_user_id, - own_device_id: self.own_device_id, - other_user_id: self.other_user_id, - state: Sent { + own_user_id: own_user_id.to_owned(), + own_device_id: own_device_id.to_owned(), + other_user_id: other_user_id.to_owned(), + state: Created { methods: SUPPORTED_METHODS.to_vec(), - flow_id: response.event_id.clone(), + flow_id: flow_id.to_owned(), }, } } -} -#[derive(Clone, Debug)] -struct Sent { - /// The verification methods supported by the sender. - pub methods: Vec, - - /// The event id of our `m.key.verification.request` event which acts as an - /// unique id identifying this verification flow. - pub flow_id: EventId, -} - -impl RequestState { - fn into_ready(self, _sender: &UserId, content: &ReadyEventContent) -> RequestState { + fn into_ready(self, _sender: &UserId, content: ReadyContent) -> RequestState { // TODO check the flow id, and that the methods match what we suggested. RequestState { own_user_id: self.own_user_id, own_device_id: self.own_device_id, other_user_id: self.other_user_id, state: Ready { - methods: content.methods.to_owned(), - other_device_id: content.from_device.clone(), + methods: content.methods().to_owned(), + other_device_id: content.from_device().into(), flow_id: self.state.flow_id, }, } } } +#[derive(Clone, Debug)] +struct Created { + /// The verification methods supported by the sender. + pub methods: Vec, + + /// The event id of our `m.key.verification.request` event which acts as an + /// unique id identifying this verification flow. + pub flow_id: FlowId, +} + #[derive(Clone, Debug)] struct Requested { /// The verification methods supported by the sender. @@ -327,7 +484,7 @@ struct Requested { /// The event id of the `m.key.verification.request` event which acts as an /// unique id identifying this verification flow. - pub flow_id: EventId, + pub flow_id: FlowId, /// The device id of the device that responded to the verification request. pub other_device_id: DeviceIdBox, @@ -338,8 +495,8 @@ impl RequestState { own_user_id: &UserId, own_device_id: &DeviceId, sender: &UserId, - event_id: &EventId, - content: &KeyVerificationRequestEventContent, + flow_id: &FlowId, + content: RequestContent, ) -> RequestState { // TODO only create this if we suport the methods RequestState { @@ -347,14 +504,14 @@ impl RequestState { own_device_id: own_device_id.into(), other_user_id: sender.clone(), state: Requested { - methods: content.methods.clone(), - flow_id: event_id.clone(), - other_device_id: content.from_device.clone(), + methods: content.methods().to_owned(), + flow_id: flow_id.clone(), + other_device_id: content.from_device().into(), }, } } - fn accept(self) -> (RequestState, ReadyEventContent) { + fn accept(self) -> (RequestState, OutgoingContent) { let state = RequestState { own_user_id: self.own_user_id, own_device_id: self.own_device_id.clone(), @@ -366,12 +523,24 @@ impl RequestState { }, }; - let content = ReadyEventContent { - from_device: self.own_device_id, - methods: self.state.methods, - relation: Relation { - event_id: self.state.flow_id, - }, + let content = match self.state.flow_id { + FlowId::ToDevice(i) => { + AnyToDeviceEventContent::KeyVerificationReady(ReadyToDeviceEventContent { + from_device: self.own_device_id, + methods: self.state.methods, + transaction_id: i, + }) + .into() + } + FlowId::InRoom(r, e) => ( + r, + AnyMessageEventContent::KeyVerificationReady(ReadyEventContent { + from_device: self.own_device_id, + methods: self.state.methods, + relation: Relation { event_id: e }, + }), + ) + .into(), }; (state, content) @@ -388,7 +557,7 @@ struct Ready { /// The event id of the `m.key.verification.request` event which acts as an /// unique id identifying this verification flow. - pub flow_id: EventId, + pub flow_id: FlowId, } impl RequestState { @@ -413,22 +582,31 @@ impl RequestState { fn start_sas( self, - room_id: &RoomId, store: Arc>, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, other_identity: Option, ) -> (Sas, StartContent) { - Sas::start_in_room( - self.state.flow_id, - room_id.clone(), - account, - private_identity, - other_device, - store, - other_identity, - ) + match self.state.flow_id { + FlowId::ToDevice(t) => Sas::start( + account, + private_identity, + other_device, + store, + other_identity, + Some(t), + ), + FlowId::InRoom(r, e) => Sas::start_in_room( + e, + r, + account, + private_identity, + other_device, + store, + other_identity, + ), + } } } @@ -447,7 +625,6 @@ mod test { use std::{convert::TryFrom, time::SystemTime}; use matrix_sdk_common::{ - api::r0::message::send_message_event::Response as RoomMessageResponse, events::{SyncMessageEvent, Unsigned}, identifiers::{event_id, room_id, DeviceIdBox, UserId}, }; @@ -456,7 +633,10 @@ mod test { use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, store::{CryptoStore, MemoryStore}, - verification::sas::StartContent, + verification::{ + requests::ReadyContent, + sas::{OutgoingContent, StartContent}, + }, ReadOnlyDevice, }; @@ -491,32 +671,31 @@ mod test { let bob_store: Box = Box::new(MemoryStore::new()); let bob_identity = PrivateCrossSigningIdentity::empty(alice_id()); + let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id()); + let bob_request = VerificationRequest::new( bob, bob_identity, bob_store.into(), - room_id.clone().into(), + &room_id, + &event_id, &alice_id(), ); - let content = bob_request.request().unwrap(); - - let alice_request = VerificationRequest::from_request_event( + let alice_request = VerificationRequest::from_room_request( alice, alice_identity, alice_store.into(), - &room_id, &bob_id(), &event_id, + &room_id, &content, ); - let content = alice_request.accept().unwrap(); + let content: OutgoingContent = alice_request.accept().unwrap().into(); + let content = ReadyContent::try_from(&content).unwrap(); - let response = RoomMessageResponse::new(event_id); - bob_request.mark_as_sent(&response); - - bob_request.receive_ready(&alice_id(), &content).unwrap(); + bob_request.receive_ready(&alice_id(), content).unwrap(); assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); @@ -538,32 +717,31 @@ mod test { let bob_store: Box = Box::new(MemoryStore::new()); let bob_identity = PrivateCrossSigningIdentity::empty(alice_id()); + let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id()); + let bob_request = VerificationRequest::new( bob, bob_identity, bob_store.into(), - room_id.clone().into(), + &room_id, + &event_id, &alice_id(), ); - let content = bob_request.request().unwrap(); - - let alice_request = VerificationRequest::from_request_event( + let alice_request = VerificationRequest::from_room_request( alice, alice_identity, alice_store.into(), - &room_id, &bob_id(), &event_id, + &room_id, &content, ); - let content = alice_request.accept().unwrap(); + let content: OutgoingContent = alice_request.accept().unwrap().into(); + let content = ReadyContent::try_from(&content).unwrap(); - let response = RoomMessageResponse::new(event_id.clone()); - bob_request.mark_as_sent(&response); - - bob_request.receive_ready(&alice_id(), &content).unwrap(); + bob_request.receive_ready(&alice_id(), content).unwrap(); assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); diff --git a/matrix_sdk_crypto/src/verification/sas/event_enums.rs b/matrix_sdk_crypto/src/verification/sas/event_enums.rs index ce122eac..6cae021b 100644 --- a/matrix_sdk_crypto/src/verification/sas/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/sas/event_enums.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(dead_code)] - use std::{collections::BTreeMap, convert::TryInto}; use matrix_sdk_common::{ @@ -276,3 +274,71 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent { OutgoingContent::Room(content.0, content.1) } } + +#[cfg(test)] +use crate::OutgoingVerificationRequest; + +#[cfg(test)] +impl From for OutgoingContent { + fn from(request: OutgoingVerificationRequest) -> Self { + use matrix_sdk_common::events::EventType; + use serde_json::Value; + + match request { + OutgoingVerificationRequest::ToDevice(r) => { + let json: Value = serde_json::from_str( + r.messages + .values() + .next() + .unwrap() + .values() + .next() + .unwrap() + .get(), + ) + .unwrap(); + + match r.event_type { + EventType::KeyVerificationRequest => { + AnyToDeviceEventContent::KeyVerificationRequest( + serde_json::from_value(json).unwrap(), + ) + } + EventType::KeyVerificationReady => { + AnyToDeviceEventContent::KeyVerificationReady( + serde_json::from_value(json).unwrap(), + ) + } + EventType::KeyVerificationDone => AnyToDeviceEventContent::KeyVerificationDone( + serde_json::from_value(json).unwrap(), + ), + EventType::KeyVerificationStart => { + AnyToDeviceEventContent::KeyVerificationStart( + serde_json::from_value(json).unwrap(), + ) + } + EventType::KeyVerificationKey => AnyToDeviceEventContent::KeyVerificationKey( + serde_json::from_value(json).unwrap(), + ), + EventType::KeyVerificationAccept => { + AnyToDeviceEventContent::KeyVerificationAccept( + serde_json::from_value(json).unwrap(), + ) + } + EventType::KeyVerificationMac => AnyToDeviceEventContent::KeyVerificationMac( + serde_json::from_value(json).unwrap(), + ), + EventType::KeyVerificationCancel => { + AnyToDeviceEventContent::KeyVerificationCancel( + serde_json::from_value(json).unwrap(), + ) + } + _ => unreachable!(), + } + .into() + } + + OutgoingVerificationRequest::InRoom(r) => (r.room_id, r.content).into(), + } + } +} diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index e505eec1..4d8afd5d 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -29,7 +29,7 @@ use matrix_sdk_common::{ }, AnyToDeviceEventContent, EventType, }, - identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId}, + identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId}, uuid::Uuid, }; @@ -41,7 +41,7 @@ use crate::{ use super::{ event_enums::{MacContent, StartContent}, - sas_state::FlowId, + FlowId, }; #[derive(Clone, Debug)] @@ -562,14 +562,14 @@ fn bytes_to_decimal(bytes: Vec) -> (u16, u16, u16) { pub fn content_to_request( recipient: &UserId, - recipient_device: &DeviceId, + recipient_device: impl Into, content: AnyToDeviceEventContent, ) -> ToDeviceRequest { let mut messages = BTreeMap::new(); let mut user_messages = BTreeMap::new(); user_messages.insert( - DeviceIdOrAllDevices::DeviceId(recipient_device.into()), + recipient_device.into(), serde_json::value::to_raw_value(&content).expect("Can't serialize to-device content"), ); messages.insert(recipient.clone(), user_messages); @@ -580,6 +580,8 @@ pub fn content_to_request( AnyToDeviceEventContent::KeyVerificationKey(_) => EventType::KeyVerificationKey, AnyToDeviceEventContent::KeyVerificationMac(_) => EventType::KeyVerificationMac, AnyToDeviceEventContent::KeyVerificationCancel(_) => EventType::KeyVerificationCancel, + AnyToDeviceEventContent::KeyVerificationReady(_) => EventType::KeyVerificationReady, + AnyToDeviceEventContent::KeyVerificationDone(_) => EventType::KeyVerificationDone, _ => unreachable!(), }; diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index b43e57cb..025716fe 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -33,10 +33,10 @@ use crate::{ use super::{ event_enums::{AcceptContent, CancelContent, MacContent, OutgoingContent}, sas_state::{ - Accepted, Canceled, Confirmed, Created, Done, FlowId, KeyReceived, MacReceived, SasState, - Started, WaitingForDone, + Accepted, Canceled, Confirmed, Created, Done, KeyReceived, MacReceived, SasState, Started, + WaitingForDone, }, - StartContent, + FlowId, StartContent, }; #[derive(Clone, Debug)] @@ -58,8 +58,9 @@ impl InnerSas { account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, + transaction_id: Option, ) -> (InnerSas, StartContent) { - let sas = SasState::::new(account, other_device, other_identity); + let sas = SasState::::new(account, other_device, other_identity, transaction_id); let content = sas.as_content(); (InnerSas::Created(sas), content) } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 8b9da496..e93ae102 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -47,13 +47,11 @@ use crate::{ ReadOnlyAccount, ToDeviceRequest, }; +use super::FlowId; + +pub use event_enums::{CancelContent, OutgoingContent, StartContent}; pub use helpers::content_to_request; use inner_sas::InnerSas; -pub use sas_state::FlowId; - -pub use event_enums::{OutgoingContent, StartContent}; - -use self::event_enums::CancelContent; #[derive(Debug)] /// A result of a verification flow. @@ -159,11 +157,13 @@ impl Sas { other_device: ReadOnlyDevice, store: Arc>, other_identity: Option, + transaction_id: Option, ) -> (Sas, StartContent) { let (inner, content) = InnerSas::start( account.clone(), other_device.clone(), other_identity.clone(), + transaction_id, ); ( @@ -189,7 +189,6 @@ impl Sas { /// /// Returns the new `Sas` object and a `StartEventContent` that needs to be /// sent out through the server to the other device. - #[allow(dead_code)] pub(crate) fn start_in_room( flow_id: EventId, room_id: RoomId, @@ -685,7 +684,11 @@ impl Sas { } pub(crate) fn content_to_request(&self, content: AnyToDeviceEventContent) -> ToDeviceRequest { - content_to_request(self.other_user_id(), self.other_device_id(), content) + content_to_request( + self.other_user_id(), + self.other_device_id().to_owned(), + content, + ) } } @@ -793,6 +796,7 @@ mod test { bob_device, alice_store, None, + None, ); let bob = Sas::from_start_event( diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 5603aef1..63f06541 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -54,6 +54,7 @@ use super::{ use crate::{ identities::{ReadOnlyDevice, UserIdentities}, + verification::FlowId, ReadOnlyAccount, }; @@ -72,29 +73,6 @@ const MAX_AGE: Duration = Duration::from_secs(60 * 5); // The max time a SAS object will wait for a new event to arrive. const MAX_EVENT_TIMEOUT: Duration = Duration::from_secs(60); -#[derive(Clone, Debug)] -pub enum FlowId { - ToDevice(String), - InRoom(RoomId, EventId), -} - -impl FlowId { - pub fn room_id(&self) -> Option<&RoomId> { - if let FlowId::InRoom(r, _) = &self { - Some(r) - } else { - None - } - } - - pub fn as_str(&self) -> &str { - match self { - FlowId::InRoom(_, r) => r.as_str(), - FlowId::ToDevice(t) => t.as_str(), - } - } -} - /// Struct containing the protocols that were agreed to be used for the SAS /// flow. #[derive(Clone, Debug)] @@ -386,8 +364,10 @@ impl SasState { account: ReadOnlyAccount, other_device: ReadOnlyDevice, other_identity: Option, + transaction_id: Option, ) -> SasState { - let flow_id = FlowId::ToDevice(Uuid::new_v4().to_string()); + let flow_id = + FlowId::ToDevice(transaction_id.unwrap_or_else(|| Uuid::new_v4().to_string())); Self::new_helper(flow_id, account, other_device, other_identity) } @@ -1259,7 +1239,7 @@ mod test { let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id()); let bob_device = ReadOnlyDevice::from_account(&bob).await; - let alice_sas = SasState::::new(alice.clone(), bob_device, None); + let alice_sas = SasState::::new(alice.clone(), bob_device, None, None); let start_content = alice_sas.as_content(); @@ -1430,7 +1410,7 @@ mod test { let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id()); let bob_device = ReadOnlyDevice::from_account(&bob).await; - let alice_sas = SasState::::new(alice.clone(), bob_device, None); + let alice_sas = SasState::::new(alice.clone(), bob_device, None, None); let mut start_content = alice_sas.as_content();