From da4876acee468865aa6b028415b81d19474e88e6 Mon Sep 17 00:00:00 2001 From: Johannes Becker Date: Tue, 15 Jun 2021 12:09:01 +0200 Subject: [PATCH 01/88] appservice: Rename Appservice to AppService --- matrix_sdk/src/client.rs | 2 +- .../examples/appservice_autojoin.rs | 18 +++--- matrix_sdk_appservice/src/lib.rs | 56 +++++++++---------- matrix_sdk_appservice/src/webserver/actix.rs | 10 ++-- matrix_sdk_appservice/src/webserver/warp.rs | 20 +++---- matrix_sdk_appservice/tests/tests.rs | 12 ++-- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index f1588313..4055d0de 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -501,7 +501,7 @@ impl RequestConfig { /// All outgoing http requests will have a GET query key-value appended with /// `user_id` being the key and the `user_id` from the `Session` being /// the value. Will error if there's no `Session`. This is called - /// [identity assertion] in the Matrix Appservice Spec + /// [identity assertion] in the Matrix Application Service Spec /// /// [identity assertion]: https://spec.matrix.org/unstable/application-service-api/#identity-assertion #[cfg(feature = "appservice")] diff --git a/matrix_sdk_appservice/examples/appservice_autojoin.rs b/matrix_sdk_appservice/examples/appservice_autojoin.rs index 0fbd99f7..fddba03c 100644 --- a/matrix_sdk_appservice/examples/appservice_autojoin.rs +++ b/matrix_sdk_appservice/examples/appservice_autojoin.rs @@ -11,16 +11,16 @@ use matrix_sdk_appservice::{ room::Room, EventHandler, }, - Appservice, AppserviceRegistration, + AppService, AppServiceRegistration, }; use tracing::{error, trace}; -struct AppserviceEventHandler { - appservice: Appservice, +struct AppServiceEventHandler { + appservice: AppService, } -impl AppserviceEventHandler { - pub fn new(appservice: Appservice) -> Self { +impl AppServiceEventHandler { + pub fn new(appservice: AppService) -> Self { Self { appservice } } @@ -47,7 +47,7 @@ impl AppserviceEventHandler { } #[async_trait] -impl EventHandler for AppserviceEventHandler { +impl EventHandler for AppServiceEventHandler { async fn on_room_member(&self, room: Room, event: &SyncStateEvent) { match self.handle_room_member(room, event).await { Ok(_) => (), @@ -63,10 +63,10 @@ pub async fn main() -> Result<(), Box> { let homeserver_url = "http://localhost:8008"; let server_name = "localhost"; - let registration = AppserviceRegistration::try_from_yaml_file("./tests/registration.yaml")?; + let registration = AppServiceRegistration::try_from_yaml_file("./tests/registration.yaml")?; - let mut appservice = Appservice::new(homeserver_url, server_name, registration).await?; - appservice.set_event_handler(Box::new(AppserviceEventHandler::new(appservice.clone()))).await?; + let mut appservice = AppService::new(homeserver_url, server_name, registration).await?; + appservice.set_event_handler(Box::new(AppServiceEventHandler::new(appservice.clone()))).await?; let (host, port) = appservice.registration().get_host_and_port()?; appservice.run(host, port).await?; diff --git a/matrix_sdk_appservice/src/lib.rs b/matrix_sdk_appservice/src/lib.rs index d94eff1e..6a7f6347 100644 --- a/matrix_sdk_appservice/src/lib.rs +++ b/matrix_sdk_appservice/src/lib.rs @@ -41,11 +41,11 @@ //! # #[async_trait] //! # impl EventHandler for MyEventHandler {} //! # -//! use matrix_sdk_appservice::{Appservice, AppserviceRegistration}; +//! use matrix_sdk_appservice::{AppService, AppServiceRegistration}; //! //! let homeserver_url = "http://127.0.0.1:8008"; //! let server_name = "localhost"; -//! let registration = AppserviceRegistration::try_from_yaml_str( +//! let registration = AppServiceRegistration::try_from_yaml_str( //! r" //! id: appservice //! url: http://127.0.0.1:9009 @@ -58,7 +58,7 @@ //! regex: '@_appservice_.*' //! ")?; //! -//! let mut appservice = Appservice::new(homeserver_url, server_name, registration).await?; +//! let mut appservice = AppService::new(homeserver_url, server_name, registration).await?; //! appservice.set_event_handler(Box::new(MyEventHandler)).await?; //! //! let (host, port) = appservice.registration().get_host_and_port()?; @@ -112,15 +112,15 @@ pub type Result = std::result::Result; pub type Host = String; pub type Port = u16; -/// Appservice Registration +/// AppService Registration /// /// Wrapper around [`Registration`] #[derive(Debug, Clone)] -pub struct AppserviceRegistration { +pub struct AppServiceRegistration { inner: Registration, } -impl AppserviceRegistration { +impl AppServiceRegistration { /// Try to load registration from yaml string /// /// See the fields of [`Registration`] for the required format @@ -158,13 +158,13 @@ impl AppserviceRegistration { } } -impl From for AppserviceRegistration { +impl From for AppServiceRegistration { fn from(value: Registration) -> Self { Self { inner: value } } } -impl Deref for AppserviceRegistration { +impl Deref for AppServiceRegistration { type Target = Registration; fn deref(&self) -> &Self::Target { @@ -175,7 +175,7 @@ impl Deref for AppserviceRegistration { type Localpart = String; /// The `localpart` of the user associated with the application service via -/// `sender_localpart` in [`AppserviceRegistration`]. +/// `sender_localpart` in [`AppServiceRegistration`]. /// /// Dummy type for shared documentation #[allow(dead_code)] @@ -183,23 +183,23 @@ pub type MainUser = (); /// The application service may specify the virtual user to act as through use /// of a user_id query string parameter on the request. The user specified in -/// the query string must be covered by one of the [`AppserviceRegistration`]'s +/// the query string must be covered by one of the [`AppServiceRegistration`]'s /// `users` namespaces. /// /// Dummy type for shared documentation pub type VirtualUser = (); -/// Appservice +/// AppService #[derive(Debug, Clone)] -pub struct Appservice { +pub struct AppService { homeserver_url: Url, server_name: ServerNameBox, - registration: Arc, + registration: Arc, clients: Arc>, } -impl Appservice { - /// Create new Appservice +impl AppService { + /// Create new AppService /// /// Also creates and caches a [`Client`] for the [`MainUser`]. /// The default [`ClientConfig`] is used, if you want to customize it @@ -210,14 +210,14 @@ impl Appservice { /// * `homeserver_url` - The homeserver that the client should connect to. /// * `server_name` - The server name to use when constructing user ids from /// the localpart. - /// * `registration` - The [Appservice Registration] to use when interacting + /// * `registration` - The [AppService Registration] to use when interacting /// with the homeserver. /// - /// [Appservice Registration]: https://matrix.org/docs/spec/application_service/r0.1.2#registration + /// [AppService Registration]: https://matrix.org/docs/spec/application_service/r0.1.2#registration pub async fn new( homeserver_url: impl TryInto, server_name: impl TryInto, - registration: AppserviceRegistration, + registration: AppServiceRegistration, ) -> Result { let appservice = Self::new_with_config( homeserver_url, @@ -235,7 +235,7 @@ impl Appservice { pub async fn new_with_config( homeserver_url: impl TryInto, server_name: impl TryInto, - registration: AppserviceRegistration, + registration: AppServiceRegistration, client_config: ClientConfig, ) -> Result { let homeserver_url = homeserver_url.try_into()?; @@ -244,7 +244,7 @@ impl Appservice { let clients = Arc::new(DashMap::new()); let sender_localpart = registration.sender_localpart.clone(); - let appservice = Appservice { homeserver_url, server_name, registration, clients }; + let appservice = AppService { homeserver_url, server_name, registration, clients }; // we create and cache the [`MainUser`] by default appservice.create_and_cache_client(&sender_localpart, client_config).await?; @@ -354,12 +354,12 @@ impl Appservice { /// Convenience wrapper around [`Client::set_event_handler()`] that attaches /// the event handler to the [`MainUser`]'s [`Client`] /// - /// Note that the event handler in the [`Appservice`] context only triggers + /// Note that the event handler in the [`AppService`] context only triggers /// [`join` room `timeline` events], so no state events or events from the /// `invite`, `knock` or `leave` scope. The rationale behind that is - /// that incoming Appservice transactions from the homeserver are not + /// that incoming AppService transactions from the homeserver are not /// necessarily bound to a specific user but can cover a multitude of - /// namespaces, and as such the Appservice basically only "observes + /// namespaces, and as such the AppService basically only "observes /// joined rooms". Also currently homeservers only push PDUs to appservices, /// no EDUs. There's the open [MSC2409] regarding supporting EDUs in the /// future, though it seems to be planned to put EDUs into a different @@ -410,10 +410,10 @@ impl Appservice { Ok(()) } - /// Get the Appservice [registration] + /// Get the AppService [registration] /// /// [registration]: https://matrix.org/docs/spec/application_service/r0.1.2#registration - pub fn registration(&self) -> &AppserviceRegistration { + pub fn registration(&self) -> &AppServiceRegistration { &self.registration } @@ -424,11 +424,11 @@ impl Appservice { self.registration.hs_token == hs_token.as_ref() } - /// Check if given `user_id` is in any of the [`AppserviceRegistration`]'s + /// Check if given `user_id` is in any of the [`AppServiceRegistration`]'s /// `users` namespaces pub fn user_id_is_in_namespace(&self, user_id: impl AsRef) -> Result { for user in &self.registration.namespaces.users { - // TODO: precompile on Appservice construction + // TODO: precompile on AppService construction let re = Regex::new(&user.regex)?; if re.is_match(user_id.as_ref()) { return Ok(true); @@ -477,7 +477,7 @@ impl Appservice { pub async fn run(&self, host: impl Into, port: impl Into) -> Result<()> { let host = host.into(); let port = port.into(); - info!("Starting Appservice on {}:{}", &host, &port); + info!("Starting AppService on {}:{}", &host, &port); #[cfg(feature = "actix")] { diff --git a/matrix_sdk_appservice/src/webserver/actix.rs b/matrix_sdk_appservice/src/webserver/actix.rs index b983f92e..5c22643d 100644 --- a/matrix_sdk_appservice/src/webserver/actix.rs +++ b/matrix_sdk_appservice/src/webserver/actix.rs @@ -26,10 +26,10 @@ use futures::Future; use futures_util::TryStreamExt; use ruma::api::appservice as api; -use crate::{error::Error, Appservice}; +use crate::{error::Error, AppService}; pub async fn run_server( - appservice: Appservice, + appservice: AppService, host: impl Into, port: impl Into, ) -> Result<(), Error> { @@ -55,7 +55,7 @@ pub fn configure(config: &mut actix_web::web::ServiceConfig) { #[put("/transactions/{txn_id}")] async fn push_transactions( request: IncomingRequest, - appservice: Data, + appservice: Data, ) -> Result { if !appservice.compare_hs_token(request.access_token) { return Ok(HttpResponse::Unauthorized().finish()); @@ -70,7 +70,7 @@ async fn push_transactions( #[get("/users/{user_id}")] async fn query_user_id( request: IncomingRequest, - appservice: Data, + appservice: Data, ) -> Result { if !appservice.compare_hs_token(request.access_token) { return Ok(HttpResponse::Unauthorized().finish()); @@ -83,7 +83,7 @@ async fn query_user_id( #[get("/rooms/{room_alias}")] async fn query_room_alias( request: IncomingRequest, - appservice: Data, + appservice: Data, ) -> Result { if !appservice.compare_hs_token(request.access_token) { return Ok(HttpResponse::Unauthorized().finish()); diff --git a/matrix_sdk_appservice/src/webserver/warp.rs b/matrix_sdk_appservice/src/webserver/warp.rs index 9a4f3504..7c584436 100644 --- a/matrix_sdk_appservice/src/webserver/warp.rs +++ b/matrix_sdk_appservice/src/webserver/warp.rs @@ -19,10 +19,10 @@ use matrix_sdk::Bytes; use serde::Serialize; use warp::{filters::BoxedFilter, path::FullPath, Filter, Rejection, Reply}; -use crate::{Appservice, Error, Result}; +use crate::{AppService, Error, Result}; pub async fn run_server( - appservice: Appservice, + appservice: AppService, host: impl Into, port: impl Into, ) -> Result<()> { @@ -37,7 +37,7 @@ pub async fn run_server( } } -pub fn warp_filter(appservice: Appservice) -> BoxedFilter<(impl Reply,)> { +pub fn warp_filter(appservice: AppService) -> BoxedFilter<(impl Reply,)> { // TODO: try to use a struct instead of needlessly cloning appservice multiple // times on every request warp::any() @@ -51,7 +51,7 @@ pub fn warp_filter(appservice: Appservice) -> BoxedFilter<(impl Reply,)> { mod filters { use super::*; - pub fn users(appservice: Appservice) -> BoxedFilter<(impl Reply,)> { + pub fn users(appservice: AppService) -> BoxedFilter<(impl Reply,)> { warp::get() .and( warp::path!("_matrix" / "app" / "v1" / "users" / String) @@ -65,7 +65,7 @@ mod filters { .boxed() } - pub fn rooms(appservice: Appservice) -> BoxedFilter<(impl Reply,)> { + pub fn rooms(appservice: AppService) -> BoxedFilter<(impl Reply,)> { warp::get() .and( warp::path!("_matrix" / "app" / "v1" / "rooms" / String) @@ -79,7 +79,7 @@ mod filters { .boxed() } - pub fn transactions(appservice: Appservice) -> BoxedFilter<(impl Reply,)> { + pub fn transactions(appservice: AppService) -> BoxedFilter<(impl Reply,)> { warp::put() .and( warp::path!("_matrix" / "app" / "v1" / "transactions" / String) @@ -93,7 +93,7 @@ mod filters { .boxed() } - fn common(appservice: Appservice) -> BoxedFilter<(Appservice, http::Request)> { + fn common(appservice: AppService) -> BoxedFilter<(AppService, http::Request)> { warp::any() .and(filters::valid_access_token(appservice.registration().hs_token.clone())) .map(move || appservice.clone()) @@ -156,7 +156,7 @@ mod handlers { pub async fn user( _user_id: String, - _appservice: Appservice, + _appservice: AppService, _request: http::Request, ) -> StdResult { Ok(warp::reply::json(&String::from("{}"))) @@ -164,7 +164,7 @@ mod handlers { pub async fn room( _room_id: String, - _appservice: Appservice, + _appservice: AppService, _request: http::Request, ) -> StdResult { Ok(warp::reply::json(&String::from("{}"))) @@ -172,7 +172,7 @@ mod handlers { pub async fn transaction( _txn_id: String, - appservice: Appservice, + appservice: AppService, request: http::Request, ) -> StdResult { let incoming_transaction: matrix_sdk::api_appservice::event::push_events::v1::IncomingRequest = diff --git a/matrix_sdk_appservice/tests/tests.rs b/matrix_sdk_appservice/tests/tests.rs index f407b1c0..c68e5ad0 100644 --- a/matrix_sdk_appservice/tests/tests.rs +++ b/matrix_sdk_appservice/tests/tests.rs @@ -19,7 +19,7 @@ fn registration_string() -> String { include_str!("../tests/registration.yaml").to_owned() } -async fn appservice(registration: Option) -> Result { +async fn appservice(registration: Option) -> Result { // env::set_var( // "RUST_LOG", // "mockito=debug,matrix_sdk=debug,ruma=debug,actix_web=debug,warp=debug", @@ -28,7 +28,7 @@ async fn appservice(registration: Option) -> Result { let registration = match registration { Some(registration) => registration.into(), - None => AppserviceRegistration::try_from_yaml_str(registration_string()).unwrap(), + None => AppServiceRegistration::try_from_yaml_str(registration_string()).unwrap(), }; let homeserver_url = mockito::server_url(); @@ -37,7 +37,7 @@ async fn appservice(registration: Option) -> Result { let client_config = ClientConfig::default().request_config(RequestConfig::default().disable_retry()); - Ok(Appservice::new_with_config( + Ok(AppService::new_with_config( homeserver_url.as_ref(), server_name, registration, @@ -355,7 +355,7 @@ mod registration { #[test] fn test_registration() -> Result<()> { let registration: Registration = serde_yaml::from_str(®istration_string())?; - let registration: AppserviceRegistration = registration.into(); + let registration: AppServiceRegistration = registration.into(); assert_eq!(registration.id, "appservice"); @@ -364,7 +364,7 @@ mod registration { #[test] fn test_registration_from_yaml_file() -> Result<()> { - let registration = AppserviceRegistration::try_from_yaml_file("./tests/registration.yaml")?; + let registration = AppServiceRegistration::try_from_yaml_file("./tests/registration.yaml")?; assert_eq!(registration.id, "appservice"); @@ -373,7 +373,7 @@ mod registration { #[test] fn test_registration_from_yaml_str() -> Result<()> { - let registration = AppserviceRegistration::try_from_yaml_str(registration_string())?; + let registration = AppServiceRegistration::try_from_yaml_str(registration_string())?; assert_eq!(registration.id, "appservice"); From 4cdb03e64b54bb82175889fc5d5ad8ebbd53f37e Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Tue, 15 Jun 2021 13:07:34 +0200 Subject: [PATCH 02/88] matrix-sdk: use media cache for avatar requests --- matrix_sdk/src/client.rs | 27 +++++++++------------------ matrix_sdk/src/room/common.rs | 33 +++++++++++++-------------------- matrix_sdk/src/room_member.rs | 34 +++++++++++++--------------------- 3 files changed, 35 insertions(+), 59 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index f1588313..f5390558 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -781,20 +781,20 @@ impl Client { /// Gets the avatar of the owner of the client, if set. /// - /// Returns the avatar. No guarantee on the size of the image is given. - /// If no size is given the full-sized avatar will be returned. + /// Returns the avatar. + /// If a thumbnail is requested no guarantee on the size of the image is + /// given. /// /// # Arguments /// - /// * `width` - The desired width of the avatar. - /// - /// * `height` - The desired height of the avatar. + /// * `format` - The desired format of the avatar. /// /// # Example /// ```no_run /// # use futures::executor::block_on; /// # use matrix_sdk::Client; /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::media::MediaFormat; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); /// # block_on(async { @@ -802,24 +802,15 @@ impl Client { /// let client = Client::new(homeserver).unwrap(); /// client.login(user, "password", None, None).await.unwrap(); /// - /// if let Some(avatar) = client.avatar(Some(96), Some(96)).await.unwrap() { + /// if let Some(avatar) = client.avatar(MediaFormat::File).await.unwrap() { /// std::fs::write("avatar.png", avatar); /// } /// # }) /// ``` - pub async fn avatar(&self, width: Option, height: Option) -> Result>> { - // TODO: try to offer the avatar from cache, requires avatar cache + pub async fn avatar(&self, format: MediaFormat) -> Result>> { if let Some(url) = self.avatar_url().await? { - if let (Some(width), Some(height)) = (width, height) { - let request = - get_content_thumbnail::Request::from_url(&url, width.into(), height.into())?; - let response = self.send(request, None).await?; - Ok(Some(response.file)) - } else { - let request = get_content::Request::from_url(&url)?; - let response = self.send(request, None).await?; - Ok(Some(response.file)) - } + let request = MediaRequest { media_type: MediaType::Uri(url), format }; + Ok(Some(self.get_media_content(&request, true).await?)) } else { Ok(None) } diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index cb425425..22fab1c8 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -4,14 +4,16 @@ use matrix_sdk_base::deserialized_responses::MembersResponse; use matrix_sdk_common::locks::Mutex; use ruma::{ api::client::r0::{ - media::{get_content, get_content_thumbnail}, membership::{get_member_events, join_room_by_id, leave_room}, message::get_message_events, }, UserId, }; -use crate::{BaseRoom, Client, Result, RoomMember}; +use crate::{ + media::{MediaFormat, MediaRequest, MediaType}, + BaseRoom, Client, Result, RoomMember, +}; /// A struct containing methods that are common for Joined, Invited and Left /// Rooms @@ -63,20 +65,20 @@ impl Common { /// Gets the avatar of this room, if set. /// - /// Returns the avatar. No guarantee on the size of the image is given. - /// If no size is given the full-sized avatar will be returned. + /// Returns the avatar. + /// If a thumbnail is requested no guarantee on the size of the image is + /// given. /// /// # Arguments /// - /// * `width` - The desired width of the avatar. - /// - /// * `height` - The desired height of the avatar. + /// * `format` - The desired format of the avatar. /// /// # Example /// ```no_run /// # use futures::executor::block_on; /// # use matrix_sdk::Client; /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::media::MediaFormat; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); /// # block_on(async { @@ -87,24 +89,15 @@ impl Common { /// let room = client /// .get_joined_room(&room_id) /// .unwrap(); - /// if let Some(avatar) = room.avatar(Some(96), Some(96)).await.unwrap() { + /// if let Some(avatar) = room.avatar(MediaFormat::File).await.unwrap() { /// std::fs::write("avatar.png", avatar); /// } /// # }) /// ``` - pub async fn avatar(&self, width: Option, height: Option) -> Result>> { - // TODO: try to offer the avatar from cache, requires avatar cache + pub async fn avatar(&self, format: MediaFormat) -> Result>> { if let Some(url) = self.avatar_url() { - if let (Some(width), Some(height)) = (width, height) { - let request = - get_content_thumbnail::Request::from_url(&url, width.into(), height.into())?; - let response = self.client.send(request, None).await?; - Ok(Some(response.file)) - } else { - let request = get_content::Request::from_url(&url)?; - let response = self.client.send(request, None).await?; - Ok(Some(response.file)) - } + let request = MediaRequest { media_type: MediaType::Uri(url.clone()), format }; + Ok(Some(self.client.get_media_content(&request, true).await?)) } else { Ok(None) } diff --git a/matrix_sdk/src/room_member.rs b/matrix_sdk/src/room_member.rs index e9d06382..4a6a5fa1 100644 --- a/matrix_sdk/src/room_member.rs +++ b/matrix_sdk/src/room_member.rs @@ -1,8 +1,9 @@ use std::ops::Deref; -use ruma::api::client::r0::media::{get_content, get_content_thumbnail}; - -use crate::{BaseRoomMember, Client, Result}; +use crate::{ + media::{MediaFormat, MediaRequest, MediaType}, + BaseRoomMember, Client, Result, +}; /// The high-level `RoomMember` representation #[derive(Debug, Clone)] @@ -26,14 +27,13 @@ impl RoomMember { /// Gets the avatar of this member, if set. /// - /// Returns the avatar. No guarantee on the size of the image is given. - /// If no size is given the full-sized avatar will be returned. + /// Returns the avatar. + /// If a thumbnail is requested no guarantee on the size of the image is + /// given. /// /// # Arguments /// - /// * `width` - The desired width of the avatar. - /// - /// * `height` - The desired height of the avatar. + /// * `format` - The desired format of the avatar. /// /// # Example /// ```no_run @@ -41,6 +41,7 @@ impl RoomMember { /// # use matrix_sdk::Client; /// # use matrix_sdk::identifiers::room_id; /// # use matrix_sdk::RoomMember; + /// # use matrix_sdk::media::MediaFormat; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); /// # block_on(async { @@ -53,24 +54,15 @@ impl RoomMember { /// .unwrap(); /// let members = room.members().await.unwrap(); /// let member = members.first().unwrap(); - /// if let Some(avatar) = member.avatar(Some(96), Some(96)).await.unwrap() { + /// if let Some(avatar) = member.avatar(MediaFormat::File).await.unwrap() { /// std::fs::write("avatar.png", avatar); /// } /// # }) /// ``` - pub async fn avatar(&self, width: Option, height: Option) -> Result>> { - // TODO: try to offer the avatar from cache, requires avatar cache + pub async fn avatar(&self, format: MediaFormat) -> Result>> { if let Some(url) = self.avatar_url() { - if let (Some(width), Some(height)) = (width, height) { - let request = - get_content_thumbnail::Request::from_url(url, width.into(), height.into())?; - let response = self.client.send(request, None).await?; - Ok(Some(response.file)) - } else { - let request = get_content::Request::from_url(url)?; - let response = self.client.send(request, None).await?; - Ok(Some(response.file)) - } + let request = MediaRequest { media_type: MediaType::Uri(url.clone()), format }; + Ok(Some(self.client.get_media_content(&request, true).await?)) } else { Ok(None) } From 533a5b92b0a0c647f5f747470cf212c80f1caa07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 8 Jun 2021 14:54:20 +0200 Subject: [PATCH 03/88] crypto: Ignore key verification requests that have an invalid timestamp --- .../src/verification/event_enums.rs | 11 ++++ matrix_sdk_crypto/src/verification/machine.rs | 65 +++++++++++++++---- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index c623a29a..895e34b3 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -35,6 +35,7 @@ use ruma::{ }, identifiers::{DeviceId, RoomId, UserId}, serde::CanonicalJsonValue, + MilliSecondsSinceUnixEpoch, }; use super::FlowId; @@ -53,6 +54,16 @@ impl AnyEvent<'_> { } } + pub fn timestamp(&self) -> Option<&MilliSecondsSinceUnixEpoch> { + match self { + AnyEvent::Room(e) => Some(e.origin_server_ts()), + AnyEvent::ToDevice(e) => match e { + AnyToDeviceEvent::KeyVerificationRequest(e) => Some(&e.content.timestamp), + _ => None, + }, + } + } + pub fn verification_content(&self) -> Option { match self { AnyEvent::Room(e) => match e { diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 371c78de..b4a7da22 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -16,8 +16,8 @@ use std::{convert::TryFrom, sync::Arc}; use dashmap::DashMap; use matrix_sdk_common::{locks::Mutex, uuid::Uuid}; -use ruma::{DeviceId, UserId}; -use tracing::{info, warn}; +use ruma::{uint, DeviceId, MilliSecondsSinceUnixEpoch, UInt, UserId}; +use tracing::{info, trace, warn}; use super::{ cache::VerificationCache, @@ -98,6 +98,30 @@ impl VerificationMachine { self.verifications.get_sas(transaction_id) } + #[cfg(not(target_arch = "wasm32"))] + fn is_timestamp_valid(timestamp: &MilliSecondsSinceUnixEpoch) -> bool { + // The event should be ignored if the event is older than 10 minutes + let old_timestamp_threshold: UInt = uint!(600); + // The event should be ignored if the event is 5 minutes or more into the + // future. + let timestamp_threshold: UInt = uint!(300); + + let timestamp = timestamp.as_secs(); + let now = MilliSecondsSinceUnixEpoch::now().as_secs(); + + !(now.saturating_sub(timestamp) > old_timestamp_threshold + || timestamp.saturating_sub(now) > timestamp_threshold) + } + + #[cfg(target_arch = "wasm32")] + fn is_timestamp_valid(timestamp: &MilliSecondsSinceUnixEpoch) -> bool { + // TODO the non-wasm method with the same name uses + // `MilliSecondsSinceUnixEpoch::now()` which internally uses + // `SystemTime::now()` this panics under WASM, thus we're returning here + // true for now. + true + } + fn queue_up_content( &self, recipient: &UserId, @@ -184,17 +208,34 @@ impl VerificationMachine { "Received a new verification request", ); - let request = VerificationRequest::from_request( - self.verifications.clone(), - self.account.clone(), - self.private_identity.lock().await.clone(), - self.store.clone(), - event.sender(), - flow_id, - r, - ); + if let Some(timestamp) = event.timestamp() { + if Self::is_timestamp_valid(timestamp) { + let request = VerificationRequest::from_request( + self.verifications.clone(), + self.account.clone(), + self.private_identity.lock().await.clone(), + self.store.clone(), + event.sender(), + flow_id, + r, + ); - self.requests.insert(request.flow_id().as_str().to_owned(), request); + self.requests.insert(request.flow_id().as_str().to_owned(), request); + } else { + trace!( + sender = event.sender().as_str(), + from_device = r.from_device().as_str(), + timestamp =? timestamp, + "The received verification request was too old or too far into the future", + ); + } + } else { + warn!( + sender = event.sender().as_str(), + from_device = r.from_device().as_str(), + "The key verification request didn't contain a valid timestamp" + ); + } } AnyVerificationContent::Cancel(c) => { if let Some(verification) = self.get_request(flow_id.as_str()) { From ada71586ac0a7c6281103a3cd6fcfc5f1322b7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 8 Jun 2021 16:13:14 +0200 Subject: [PATCH 04/88] crypto: Scope the verifications per sender --- matrix_sdk/examples/emoji_verification.rs | 16 ++++-- matrix_sdk/src/client.rs | 4 +- matrix_sdk_base/src/client.rs | 9 ++- matrix_sdk_crypto/src/machine.rs | 14 +++-- matrix_sdk_crypto/src/verification/cache.rs | 57 +++++++++++++------ matrix_sdk_crypto/src/verification/machine.rs | 31 ++++++---- matrix_sdk_crypto/src/verification/mod.rs | 22 +++++++ .../src/verification/requests.rs | 6 +- 8 files changed, 114 insertions(+), 45 deletions(-) diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 72e8b7cd..967154f4 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -81,7 +81,7 @@ async fn login( match event { AnyToDeviceEvent::KeyVerificationStart(e) => { let sas = client - .get_verification(&e.content.transaction_id) + .get_verification(&e.sender, &e.content.transaction_id) .await .expect("Sas object wasn't created"); println!( @@ -95,7 +95,7 @@ async fn login( AnyToDeviceEvent::KeyVerificationKey(e) => { let sas = client - .get_verification(&e.content.transaction_id) + .get_verification(&e.sender, &e.content.transaction_id) .await .expect("Sas object wasn't created"); @@ -104,7 +104,7 @@ async fn login( AnyToDeviceEvent::KeyVerificationMac(e) => { let sas = client - .get_verification(&e.content.transaction_id) + .get_verification(&e.sender, &e.content.transaction_id) .await .expect("Sas object wasn't created"); @@ -141,7 +141,10 @@ async fn login( } AnySyncMessageEvent::KeyVerificationKey(e) => { let sas = client - .get_verification(e.content.relation.event_id.as_str()) + .get_verification( + &e.sender, + e.content.relation.event_id.as_str(), + ) .await .expect("Sas object wasn't created"); @@ -149,7 +152,10 @@ async fn login( } AnySyncMessageEvent::KeyVerificationMac(e) => { let sas = client - .get_verification(e.content.relation.event_id.as_str()) + .get_verification( + &e.sender, + e.content.relation.event_id.as_str(), + ) .await .expect("Sas object wasn't created"); diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 46359771..a5efaf9d 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -2185,9 +2185,9 @@ impl Client { /// Get a `Sas` verification object with the given flow id. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] - pub async fn get_verification(&self, flow_id: &str) -> Option { + pub async fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { self.base_client - .get_verification(flow_id) + .get_verification(user_id, flow_id) .await .map(|sas| Sas { inner: sas, client: self.clone() }) } diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 78853077..0b16beff 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -1213,8 +1213,13 @@ impl BaseClient { /// *m.key.verification.start* event. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] - pub async fn get_verification(&self, flow_id: &str) -> Option { - self.olm.lock().await.as_ref().and_then(|o| o.get_verification(flow_id)) + pub async fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { + self.olm + .lock() + .await + .as_ref() + .and_then(|o| o.get_verification(user_id, flow_id).map(|v| v.sas_v1())) + .flatten() } /// Get a specific device of a user. diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 807d13cf..6abde4de 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -59,7 +59,7 @@ use crate::{ Changes, CryptoStore, DeviceChanges, IdentityChanges, MemoryStore, Result as StoreResult, Store, }, - verification::{Sas, VerificationMachine, VerificationRequest}, + verification::{Verification, VerificationMachine, VerificationRequest}, ToDeviceRequest, }; @@ -717,9 +717,9 @@ impl OlmMachine { Ok(()) } - /// Get a `Sas` verification object with the given flow id. - pub fn get_verification(&self, flow_id: &str) -> Option { - self.verification_machine.get_sas(flow_id) + /// Get a verification object for the given user id with the given flow id. + pub fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { + self.verification_machine.get_verification(user_id, flow_id) } /// Get a verification request object with the given flow id. @@ -1761,7 +1761,11 @@ pub(crate) mod test { let event = request_to_event(alice.user_id(), &request.into()); bob.handle_verification_event(&event).await; - let bob_sas = bob.get_verification(alice_sas.flow_id().as_str()).unwrap(); + let bob_sas = bob + .get_verification(alice.user_id(), alice_sas.flow_id().as_str()) + .unwrap() + .sas_v1() + .unwrap(); assert!(alice_sas.emoji().is_none()); assert!(bob_sas.emoji().is_none()); diff --git a/matrix_sdk_crypto/src/verification/cache.rs b/matrix_sdk_crypto/src/verification/cache.rs index 975f9fd6..8d54a165 100644 --- a/matrix_sdk_crypto/src/verification/cache.rs +++ b/matrix_sdk_crypto/src/verification/cache.rs @@ -23,7 +23,7 @@ use crate::{OutgoingRequest, RoomMessageRequest}; #[derive(Clone, Debug)] pub struct VerificationCache { - verification: Arc>, + verification: Arc>>, outgoing_requests: Arc>, } @@ -35,11 +35,24 @@ impl VerificationCache { #[cfg(test)] #[allow(dead_code)] pub fn is_empty(&self) -> bool { - self.verification.is_empty() + self.verification.iter().all(|m| m.is_empty()) + } + + pub fn insert(&self, verification: impl Into) { + let verification = verification.into(); + + self.verification + .entry(verification.other_user().to_owned()) + .or_insert_with(DashMap::new) + .insert(verification.flow_id().to_owned(), verification); } pub fn insert_sas(&self, sas: Sas) { - self.verification.insert(sas.flow_id().as_str().to_string(), sas.into()); + self.insert(sas); + } + + pub fn get(&self, sender: &UserId, flow_id: &str) -> Option { + self.verification.get(sender).and_then(|m| m.get(flow_id).map(|v| v.clone())) } pub fn outgoing_requests(&self) -> Vec { @@ -47,28 +60,38 @@ impl VerificationCache { } pub fn garbage_collect(&self) -> Vec { - self.verification.retain(|_, s| !(s.is_done() || s.is_cancelled())); + for user_verification in self.verification.iter() { + user_verification.retain(|_, s| !(s.is_done() || s.is_cancelled())); + } + + self.verification.retain(|_, m| !m.is_empty()); self.verification .iter() - .filter_map(|s| { - #[allow(irrefutable_let_patterns)] - if let Verification::SasV1(s) = s.value() { - s.cancel_if_timed_out().map(|r| OutgoingRequest { - request_id: r.request_id(), - request: Arc::new(r.into()), + .flat_map(|v| { + let requests: Vec = v + .value() + .iter() + .filter_map(|s| { + if let Verification::SasV1(s) = s.value() { + s.cancel_if_timed_out().map(|r| OutgoingRequest { + request_id: r.request_id(), + request: Arc::new(r.into()), + }) + } else { + None + } }) - } else { - None - } + .collect(); + + requests }) .collect() } - pub fn get_sas(&self, transaction_id: &str) -> Option { - self.verification.get(transaction_id).and_then(|v| { - #[allow(irrefutable_let_patterns)] - if let Verification::SasV1(sas) = v.value() { + pub fn get_sas(&self, user_id: &UserId, flow_id: &str) -> Option { + self.get(user_id, flow_id).and_then(|v| { + if let Verification::SasV1(sas) = v { Some(sas.clone()) } else { None diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index b4a7da22..c84e39dc 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -24,7 +24,7 @@ use super::{ event_enums::{AnyEvent, AnyVerificationContent, OutgoingContent}, requests::VerificationRequest, sas::{content_to_request, Sas}, - FlowId, VerificationResult, + FlowId, Verification, VerificationResult, }; use crate::{ olm::PrivateCrossSigningIdentity, @@ -94,8 +94,12 @@ impl VerificationMachine { self.requests.get(flow_id.as_ref()).map(|s| s.clone()) } - pub fn get_sas(&self, transaction_id: &str) -> Option { - self.verifications.get_sas(transaction_id) + pub fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { + self.verifications.get(user_id, flow_id) + } + + pub fn get_sas(&self, user_id: &UserId, flow_id: &str) -> Option { + self.verifications.get_sas(user_id, flow_id) } #[cfg(not(target_arch = "wasm32"))] @@ -242,7 +246,7 @@ impl VerificationMachine { verification.receive_cancel(event.sender(), c); } - if let Some(sas) = self.verifications.get_sas(flow_id.as_str()) { + if let Some(sas) = self.get_sas(event.sender(), flow_id.as_str()) { // This won't produce an outgoing content let _ = sas.receive_any_event(event.sender(), &content); } @@ -296,7 +300,7 @@ impl VerificationMachine { } } AnyVerificationContent::Accept(_) | AnyVerificationContent::Key(_) => { - if let Some(sas) = self.verifications.get_sas(flow_id.as_str()) { + if let Some(sas) = self.get_sas(event.sender(), flow_id.as_str()) { if sas.flow_id() == &flow_id { if let Some(content) = sas.receive_any_event(event.sender(), &content) { self.queue_up_content( @@ -311,7 +315,7 @@ impl VerificationMachine { } } AnyVerificationContent::Mac(_) => { - if let Some(s) = self.verifications.get_sas(flow_id.as_str()) { + if let Some(s) = self.get_sas(event.sender(), flow_id.as_str()) { if s.flow_id() == &flow_id { let content = s.receive_any_event(event.sender(), &content); @@ -328,12 +332,15 @@ impl VerificationMachine { verification.receive_done(event.sender(), c); } - if let Some(s) = self.verifications.get_sas(flow_id.as_str()) { - let content = s.receive_any_event(event.sender(), &content); + match self.get_verification(event.sender(), flow_id.as_str()) { + Some(Verification::SasV1(sas)) => { + let content = sas.receive_any_event(event.sender(), &content); - if s.is_done() { - self.mark_sas_as_done(s, content).await?; + if sas.is_done() { + self.mark_sas_as_done(sas, content).await?; + } } + None => (), } } } @@ -426,7 +433,7 @@ mod test { async fn full_flow() { let (alice_machine, bob) = setup_verification_machine().await; - let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); + let alice = alice_machine.get_sas(bob.user_id(), bob.flow_id().as_str()).unwrap(); let request = alice.accept().unwrap(); @@ -472,7 +479,7 @@ mod test { #[tokio::test] async fn timing_out() { let (alice_machine, bob) = setup_verification_machine().await; - let alice = alice_machine.get_sas(bob.flow_id().as_str()).unwrap(); + let alice = alice_machine.get_sas(bob.user_id(), bob.flow_id().as_str()).unwrap(); assert!(!alice.timed_out()); assert!(alice_machine.verifications.outgoing_requests().is_empty()); diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 3335ec72..52b2d097 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(missing_docs)] + mod cache; mod event_enums; mod machine; @@ -57,11 +59,31 @@ impl Verification { } } + pub fn sas_v1(self) -> Option { + if let Verification::SasV1(sas) = self { + Some(sas) + } else { + None + } + } + + pub fn flow_id(&self) -> &str { + match self { + Verification::SasV1(s) => s.flow_id().as_str(), + } + } + pub fn is_cancelled(&self) -> bool { match self { Verification::SasV1(s) => s.is_cancelled(), } } + + pub fn other_user(&self) -> &UserId { + match self { + Verification::SasV1(s) => s.other_user_id(), + } + } } impl From for Verification { diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 819d2dc4..8e25cf3b 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -810,7 +810,8 @@ mod test { let content = StartContent::try_from(&start_content).unwrap(); let flow_id = content.flow_id().to_owned(); alice_request.receive_start(bob_device.user_id(), &content).await.unwrap(); - let alice_sas = alice_request.verification_cache.get_sas(&flow_id).unwrap(); + let alice_sas = + alice_request.verification_cache.get_sas(bob_device.user_id(), &flow_id).unwrap(); assert!(!bob_sas.is_cancelled()); assert!(!alice_sas.is_cancelled()); @@ -867,7 +868,8 @@ mod test { let content = StartContent::try_from(&start_content).unwrap(); let flow_id = content.flow_id().to_owned(); alice_request.receive_start(bob_device.user_id(), &content).await.unwrap(); - let alice_sas = alice_request.verification_cache.get_sas(&flow_id).unwrap(); + let alice_sas = + alice_request.verification_cache.get_sas(bob_device.user_id(), &flow_id).unwrap(); assert!(!bob_sas.is_cancelled()); assert!(!alice_sas.is_cancelled()); From 7f364fd615823a5a5f81d78a8214cdc6a04e8efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 8 Jun 2021 16:31:04 +0200 Subject: [PATCH 05/88] crypto: Allow only a identity to be verified when the verification finishes QR code based verification doesn't verify a device when users are verifying each other, generalize the logic that marks stuff as verified so we can verify either only a device or an user identity or both. --- matrix_sdk_crypto/src/verification/mod.rs | 125 ++++++++++++---------- 1 file changed, 66 insertions(+), 59 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 52b2d097..95040dff 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -43,7 +43,7 @@ use tracing::{error, info, trace, warn}; use crate::{ error::SignatureError, olm::PrivateCrossSigningIdentity, - store::{Changes, CryptoStore, DeviceChanges}, + store::{Changes, CryptoStore}, CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities, }; @@ -257,9 +257,18 @@ impl IdentitiesBeingVerified { verified_devices: Option<&[ReadOnlyDevice]>, verified_identities: Option<&[UserIdentities]>, ) -> Result { - if let Some(device) = self.mark_device_as_verified(verified_devices).await? { - let identity = self.mark_identity_as_verified(verified_identities).await?; + let device = self.mark_device_as_verified(verified_devices).await?; + let identity = self.mark_identity_as_verified(verified_identities).await?; + if device.is_none() && identity.is_none() { + // Something wen't wrong if nothing was verified, we use key + // mismatch here, since it's the closest to nothing was verified + return Ok(VerificationResult::Cancel(CancelCode::KeyMismatch)); + } + + let mut changes = Changes::default(); + + let signature_request = if let Some(device) = device { // We only sign devices of our own user here. let signature_request = if device.user_id() == self.user_id() { match self.private_identity.sign_device(&device).await { @@ -288,69 +297,67 @@ impl IdentitiesBeingVerified { None }; - let mut changes = Changes { - devices: DeviceChanges { changed: vec![device], ..Default::default() }, - ..Default::default() - }; + changes.devices.changed.push(device); + signature_request + } else { + None + }; - let identity_signature_request = if let Some(i) = identity { - // We only sign other users here. - let request = if let Some(i) = i.other() { - // Signing can fail if the user signing key is missing. - match self.private_identity.sign_user(i).await { - Ok(r) => Some(r), - Err(SignatureError::MissingSigningKey) => { - warn!( - "Can't sign the public cross signing keys for {}, \ - no private user signing key found", - i.user_id() - ); - None - } - Err(e) => { - error!( - "Error signing the public cross signing keys for {} {:?}", - i.user_id(), - e - ); - None - } + let identity_signature_request = if let Some(i) = identity { + // We only sign other users here. + let request = if let Some(i) = i.other() { + // Signing can fail if the user signing key is missing. + match self.private_identity.sign_user(i).await { + Ok(r) => Some(r), + Err(SignatureError::MissingSigningKey) => { + warn!( + "Can't sign the public cross signing keys for {}, \ + no private user signing key found", + i.user_id() + ); + None } - } else { - None - }; - - changes.identities.changed.push(i); - - request + Err(e) => { + error!( + "Error signing the public cross signing keys for {} {:?}", + i.user_id(), + e + ); + None + } + } } else { None }; - // If there are two signature upload requests, merge them. Otherwise - // use the one we have or None. - // - // Realistically at most one request will be used but let's make - // this future proof. - let merged_request = if let Some(mut r) = signature_request { - if let Some(user_request) = identity_signature_request { - r.signed_keys.extend(user_request.signed_keys); - Some(r) - } else { - Some(r) - } - } else { - identity_signature_request - }; - - // TODO store the signature upload request as well. - self.store.save_changes(changes).await?; - Ok(merged_request - .map(VerificationResult::SignatureUpload) - .unwrap_or(VerificationResult::Ok)) + changes.identities.changed.push(i); + request } else { - Ok(VerificationResult::Cancel(CancelCode::UserMismatch)) - } + None + }; + + // If there are two signature upload requests, merge them. Otherwise + // use the one we have or None. + // + // Realistically at most one request will be used but let's make + // this future proof. + let merged_request = if let Some(mut r) = signature_request { + if let Some(user_request) = identity_signature_request { + r.signed_keys.extend(user_request.signed_keys); + Some(r) + } else { + Some(r) + } + } else { + identity_signature_request + }; + + // TODO store the signature upload request as well. + self.store.save_changes(changes).await?; + + Ok(merged_request + .map(VerificationResult::SignatureUpload) + .unwrap_or(VerificationResult::Ok)) } async fn mark_identity_as_verified( From 1c8081533dae104c3f78a627bea03c421c192d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 8 Jun 2021 16:54:56 +0200 Subject: [PATCH 06/88] qrcode: Rename the main qrcode type --- matrix_qrcode/src/lib.rs | 50 ++++++++-------- matrix_qrcode/src/types.rs | 118 ++++++++++++++++++------------------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/matrix_qrcode/src/lib.rs b/matrix_qrcode/src/lib.rs index 91e60735..bf8f18d5 100644 --- a/matrix_qrcode/src/lib.rs +++ b/matrix_qrcode/src/lib.rs @@ -20,12 +20,12 @@ //! [spec]: https://spec.matrix.org/unstable/client-server-api/#qr-code-format //! //! ```no_run -//! # use matrix_qrcode::{QrVerification, DecodingError}; +//! # use matrix_qrcode::{QrVerificationData, DecodingError}; //! # fn main() -> Result<(), DecodingError> { //! use image; //! //! let image = image::open("/path/to/my/image.png").unwrap(); -//! let result = QrVerification::from_image(image)?; +//! let result = QrVerificationData::from_image(image)?; //! # Ok(()) //! # } //! ``` @@ -55,7 +55,7 @@ pub use qrcode; #[cfg_attr(feature = "docs", doc(cfg(decode_image)))] pub use rqrr; pub use types::{ - QrVerification, SelfVerificationData, SelfVerificationNoMasterKey, VerificationData, + QrVerificationData, SelfVerificationData, SelfVerificationNoMasterKey, VerificationData, }; #[cfg(test)] @@ -70,7 +70,7 @@ mod test { #[cfg(feature = "decode_image")] use crate::utils::decode_qr; - use crate::{DecodingError, QrVerification}; + use crate::{DecodingError, QrVerificationData}; #[cfg(feature = "decode_image")] static VERIFICATION: &[u8; 4277] = include_bytes!("../data/verification.png"); @@ -92,9 +92,9 @@ mod test { fn decode_test() { let image = Cursor::new(VERIFICATION); let image = image::load(image, ImageFormat::Png).unwrap().to_luma8(); - let result = QrVerification::try_from(image).unwrap(); + let result = QrVerificationData::try_from(image).unwrap(); - assert!(matches!(result, QrVerification::Verification(_))); + assert!(matches!(result, QrVerificationData::Verification(_))); } #[test] @@ -102,18 +102,18 @@ mod test { fn decode_encode_cycle() { let image = Cursor::new(VERIFICATION); let image = image::load(image, ImageFormat::Png).unwrap(); - let result = QrVerification::from_image(image).unwrap(); + let result = QrVerificationData::from_image(image).unwrap(); - assert!(matches!(result, QrVerification::Verification(_))); + assert!(matches!(result, QrVerificationData::Verification(_))); let encoded = result.to_qr_code().unwrap(); let image = encoded.render::>().build(); - let second_result = QrVerification::try_from(image).unwrap(); + let second_result = QrVerificationData::try_from(image).unwrap(); assert_eq!(result, second_result); let bytes = result.to_bytes().unwrap(); - let third_result = QrVerification::from_bytes(bytes).unwrap(); + let third_result = QrVerificationData::from_bytes(bytes).unwrap(); assert_eq!(result, third_result); } @@ -123,18 +123,18 @@ mod test { fn decode_encode_cycle_self() { let image = Cursor::new(SELF_VERIFICATION); let image = image::load(image, ImageFormat::Png).unwrap(); - let result = QrVerification::try_from(image).unwrap(); + let result = QrVerificationData::try_from(image).unwrap(); - assert!(matches!(result, QrVerification::SelfVerification(_))); + assert!(matches!(result, QrVerificationData::SelfVerification(_))); let encoded = result.to_qr_code().unwrap(); let image = encoded.render::>().build(); - let second_result = QrVerification::from_luma(image).unwrap(); + let second_result = QrVerificationData::from_luma(image).unwrap(); assert_eq!(result, second_result); let bytes = result.to_bytes().unwrap(); - let third_result = QrVerification::from_bytes(bytes).unwrap(); + let third_result = QrVerificationData::from_bytes(bytes).unwrap(); assert_eq!(result, third_result); } @@ -144,18 +144,18 @@ mod test { fn decode_encode_cycle_self_no_master() { let image = Cursor::new(SELF_NO_MASTER); let image = image::load(image, ImageFormat::Png).unwrap(); - let result = QrVerification::from_image(image).unwrap(); + let result = QrVerificationData::from_image(image).unwrap(); - assert!(matches!(result, QrVerification::SelfVerificationNoMasterKey(_))); + assert!(matches!(result, QrVerificationData::SelfVerificationNoMasterKey(_))); let encoded = result.to_qr_code().unwrap(); let image = encoded.render::>().build(); - let second_result = QrVerification::try_from(image).unwrap(); + let second_result = QrVerificationData::try_from(image).unwrap(); assert_eq!(result, second_result); let bytes = result.to_bytes().unwrap(); - let third_result = QrVerification::try_from(bytes).unwrap(); + let third_result = QrVerificationData::try_from(bytes).unwrap(); assert_eq!(result, third_result); } @@ -165,35 +165,35 @@ mod test { fn decode_invalid_qr() { let qr = QrCode::new(b"NonMatrixCode").expect("Can't build a simple QR code"); let image = qr.render::>().build(); - let result = QrVerification::try_from(image); + let result = QrVerificationData::try_from(image); assert!(matches!(result, Err(DecodingError::Header))) } #[test] fn decode_invalid_header() { let data = b"NonMatrixCode"; - let result = QrVerification::from_bytes(data); + let result = QrVerificationData::from_bytes(data); assert!(matches!(result, Err(DecodingError::Header))) } #[test] fn decode_invalid_mode() { let data = b"MATRIX\x02\x03"; - let result = QrVerification::from_bytes(data); + let result = QrVerificationData::from_bytes(data); assert!(matches!(result, Err(DecodingError::Mode(3)))) } #[test] fn decode_invalid_version() { let data = b"MATRIX\x01\x03"; - let result = QrVerification::from_bytes(data); + let result = QrVerificationData::from_bytes(data); assert!(matches!(result, Err(DecodingError::Version(1)))) } #[test] fn decode_missing_data() { let data = b"MATRIX\x02\x02"; - let result = QrVerification::from_bytes(data); + let result = QrVerificationData::from_bytes(data); assert!(matches!(result, Err(DecodingError::Read(_)))) } @@ -206,7 +206,7 @@ mod test { BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ SECRET"; - let result = QrVerification::from_bytes(data); + let result = QrVerificationData::from_bytes(data); assert!(matches!(result, Err(DecodingError::SharedSecret(_)))) } @@ -219,7 +219,7 @@ mod test { BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ SECRETISLONGENOUGH"; - let result = QrVerification::from_bytes(data); + let result = QrVerificationData::from_bytes(data); assert!(matches!(result, Err(DecodingError::Identifier(_)))) } } diff --git a/matrix_qrcode/src/types.rs b/matrix_qrcode/src/types.rs index 915c901a..03e38086 100644 --- a/matrix_qrcode/src/types.rs +++ b/matrix_qrcode/src/types.rs @@ -33,7 +33,7 @@ use crate::{ /// An enum representing the different modes a QR verification can be in. #[derive(Clone, Debug, PartialEq)] -pub enum QrVerification { +pub enum QrVerificationData { /// The QR verification is verifying another user Verification(VerificationData), /// The QR verification is self-verifying and the current device trusts or @@ -46,7 +46,7 @@ pub enum QrVerification { #[cfg(feature = "decode_image")] #[cfg_attr(feature = "docs", doc(cfg(decode_image)))] -impl TryFrom for QrVerification { +impl TryFrom for QrVerificationData { type Error = DecodingError; fn try_from(image: DynamicImage) -> Result { @@ -56,7 +56,7 @@ impl TryFrom for QrVerification { #[cfg(feature = "decode_image")] #[cfg_attr(feature = "docs", doc(cfg(decode_image)))] -impl TryFrom, Vec>> for QrVerification { +impl TryFrom, Vec>> for QrVerificationData { type Error = DecodingError; fn try_from(image: ImageBuffer, Vec>) -> Result { @@ -64,7 +64,7 @@ impl TryFrom, Vec>> for QrVerification { } } -impl TryFrom<&[u8]> for QrVerification { +impl TryFrom<&[u8]> for QrVerificationData { type Error = DecodingError; fn try_from(value: &[u8]) -> Result { @@ -72,7 +72,7 @@ impl TryFrom<&[u8]> for QrVerification { } } -impl TryFrom> for QrVerification { +impl TryFrom> for QrVerificationData { type Error = DecodingError; fn try_from(value: Vec) -> Result { @@ -80,8 +80,8 @@ impl TryFrom> for QrVerification { } } -impl QrVerification { - /// Decode and parse an image of a QR code into a `QrVerification` +impl QrVerificationData { + /// Decode and parse an image of a QR code into a `QrVerificationData` /// /// The image will be converted into a grey scale image before decoding is /// attempted @@ -92,12 +92,12 @@ impl QrVerification { /// /// # Example /// ```no_run - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// use image; /// /// let image = image::open("/path/to/my/image.png").unwrap(); - /// let result = QrVerification::from_image(image)?; + /// let result = QrVerificationData::from_image(image)?; /// # Ok(()) /// # } /// ``` @@ -109,7 +109,7 @@ impl QrVerification { } /// Decode and parse an grey scale image of a QR code into a - /// `QrVerification` + /// `QrVerificationData` /// /// # Arguments /// @@ -117,13 +117,13 @@ impl QrVerification { /// /// # Example /// ```no_run - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// use image; /// /// let image = image::open("/path/to/my/image.png").unwrap(); /// let image = image.to_luma8(); - /// let result = QrVerification::from_luma(image)?; + /// let result = QrVerificationData::from_luma(image)?; /// # Ok(()) /// # } /// ``` @@ -134,7 +134,7 @@ impl QrVerification { } /// Parse the decoded payload of a QR code in byte slice form as a - /// `QrVerification` + /// `QrVerificationData` /// /// This method is useful if you would like to do your own custom QR code /// decoding. @@ -145,7 +145,7 @@ impl QrVerification { /// /// # Example /// ``` - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// let data = b"MATRIX\ /// \x02\x02\x00\x07\ @@ -154,7 +154,7 @@ impl QrVerification { /// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ /// SHARED_SECRET"; /// - /// let result = QrVerification::from_bytes(data)?; + /// let result = QrVerificationData::from_bytes(data)?; /// # Ok(()) /// # } /// ``` @@ -162,9 +162,9 @@ impl QrVerification { Self::decode_bytes(bytes) } - /// Encode the `QrVerification` into a `QrCode`. + /// Encode the `QrVerificationData` into a `QrCode`. /// - /// This method turns the `QrVerification` into a QR code that can be + /// This method turns the `QrVerificationData` into a QR code that can be /// rendered and presented to be scanned. /// /// The encoding can fail if the data doesn't fit into a QR code or if the @@ -173,7 +173,7 @@ impl QrVerification { /// /// # Example /// ``` - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// let data = b"MATRIX\ /// \x02\x02\x00\x07\ @@ -182,28 +182,28 @@ impl QrVerification { /// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ /// SHARED_SECRET"; /// - /// let result = QrVerification::from_bytes(data)?; + /// let result = QrVerificationData::from_bytes(data)?; /// let encoded = result.to_qr_code().unwrap(); /// # Ok(()) /// # } /// ``` pub fn to_qr_code(&self) -> Result { match self { - QrVerification::Verification(v) => v.to_qr_code(), - QrVerification::SelfVerification(v) => v.to_qr_code(), - QrVerification::SelfVerificationNoMasterKey(v) => v.to_qr_code(), + QrVerificationData::Verification(v) => v.to_qr_code(), + QrVerificationData::SelfVerification(v) => v.to_qr_code(), + QrVerificationData::SelfVerificationNoMasterKey(v) => v.to_qr_code(), } } - /// Encode the `QrVerification` into a vector of bytes that can be encoded - /// as a QR code. + /// Encode the `QrVerificationData` into a vector of bytes that can be + /// encoded as a QR code. /// /// The encoding can fail if the identity keys that should be encoded are /// not valid base64. /// /// # Example /// ``` - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// let data = b"MATRIX\ /// \x02\x02\x00\x07\ @@ -212,7 +212,7 @@ impl QrVerification { /// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ /// SHARED_SECRET"; /// - /// let result = QrVerification::from_bytes(data)?; + /// let result = QrVerificationData::from_bytes(data)?; /// let encoded = result.to_bytes().unwrap(); /// /// assert_eq!(data.as_ref(), encoded.as_slice()); @@ -221,9 +221,9 @@ impl QrVerification { /// ``` pub fn to_bytes(&self) -> Result, EncodingError> { match self { - QrVerification::Verification(v) => v.to_bytes(), - QrVerification::SelfVerification(v) => v.to_bytes(), - QrVerification::SelfVerificationNoMasterKey(v) => v.to_bytes(), + QrVerificationData::Verification(v) => v.to_bytes(), + QrVerificationData::SelfVerification(v) => v.to_bytes(), + QrVerificationData::SelfVerificationNoMasterKey(v) => v.to_bytes(), } } @@ -289,13 +289,13 @@ impl QrVerification { return Err(DecodingError::SharedSecret(shared_secret.len())); } - QrVerification::new(mode, flow_id, first_key, second_key, shared_secret) + QrVerificationData::new(mode, flow_id, first_key, second_key, shared_secret) } /// Decode the given image of an QR code and if we find a valid code, try to /// decode it as a `QrVerification`. #[cfg(feature = "decode_image")] - fn decode(image: ImageBuffer, Vec>) -> Result { + fn decode(image: ImageBuffer, Vec>) -> Result { let decoded = decode_qr(image)?; Self::decode_bytes(decoded) } @@ -328,41 +328,41 @@ impl QrVerification { } } - /// Get the flow id for this `QrVerification`. + /// Get the flow id for this `QrVerificationData`. /// /// This represents the ID as a string even if it is a `EventId`. pub fn flow_id(&self) -> &str { match self { - QrVerification::Verification(v) => v.event_id.as_str(), - QrVerification::SelfVerification(v) => &v.transaction_id, - QrVerification::SelfVerificationNoMasterKey(v) => &v.transaction_id, + QrVerificationData::Verification(v) => v.event_id.as_str(), + QrVerificationData::SelfVerification(v) => &v.transaction_id, + QrVerificationData::SelfVerificationNoMasterKey(v) => &v.transaction_id, } } - /// Get the first key of this `QrVerification`. + /// Get the first key of this `QrVerificationData`. pub fn first_key(&self) -> &str { match self { - QrVerification::Verification(v) => &v.first_master_key, - QrVerification::SelfVerification(v) => &v.master_key, - QrVerification::SelfVerificationNoMasterKey(v) => &v.device_key, + QrVerificationData::Verification(v) => &v.first_master_key, + QrVerificationData::SelfVerification(v) => &v.master_key, + QrVerificationData::SelfVerificationNoMasterKey(v) => &v.device_key, } } - /// Get the second key of this `QrVerification`. + /// Get the second key of this `QrVerificationData`. pub fn second_key(&self) -> &str { match self { - QrVerification::Verification(v) => &v.second_master_key, - QrVerification::SelfVerification(v) => &v.device_key, - QrVerification::SelfVerificationNoMasterKey(v) => &v.master_key, + QrVerificationData::Verification(v) => &v.second_master_key, + QrVerificationData::SelfVerification(v) => &v.device_key, + QrVerificationData::SelfVerificationNoMasterKey(v) => &v.master_key, } } - /// Get the secret of this `QrVerification`. + /// Get the secret of this `QrVerificationData`. pub fn secret(&self) -> &str { match self { - QrVerification::Verification(v) => &v.shared_secret, - QrVerification::SelfVerification(v) => &v.shared_secret, - QrVerification::SelfVerificationNoMasterKey(v) => &v.shared_secret, + QrVerificationData::Verification(v) => &v.shared_secret, + QrVerificationData::SelfVerification(v) => &v.shared_secret, + QrVerificationData::SelfVerificationNoMasterKey(v) => &v.shared_secret, } } } @@ -412,7 +412,7 @@ impl VerificationData { /// /// # Example /// ``` - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// let data = b"MATRIX\ /// \x02\x00\x00\x0f\ @@ -421,8 +421,8 @@ impl VerificationData { /// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ /// SHARED_SECRET"; /// - /// let result = QrVerification::from_bytes(data)?; - /// if let QrVerification::Verification(decoded) = result { + /// let result = QrVerificationData::from_bytes(data)?; + /// if let QrVerificationData::Verification(decoded) = result { /// let encoded = decoded.to_bytes().unwrap(); /// assert_eq!(data.as_ref(), encoded.as_slice()); /// } else { @@ -459,7 +459,7 @@ impl VerificationData { } } -impl From for QrVerification { +impl From for QrVerificationData { fn from(data: VerificationData) -> Self { Self::Verification(data) } @@ -515,7 +515,7 @@ impl SelfVerificationData { /// /// # Example /// ``` - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// let data = b"MATRIX\ /// \x02\x01\x00\x06\ @@ -524,8 +524,8 @@ impl SelfVerificationData { /// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ /// SHARED_SECRET"; /// - /// let result = QrVerification::from_bytes(data)?; - /// if let QrVerification::SelfVerification(decoded) = result { + /// let result = QrVerificationData::from_bytes(data)?; + /// if let QrVerificationData::SelfVerification(decoded) = result { /// let encoded = decoded.to_bytes().unwrap(); /// assert_eq!(data.as_ref(), encoded.as_slice()); /// } else { @@ -562,7 +562,7 @@ impl SelfVerificationData { } } -impl From for QrVerification { +impl From for QrVerificationData { fn from(data: SelfVerificationData) -> Self { Self::SelfVerification(data) } @@ -618,7 +618,7 @@ impl SelfVerificationNoMasterKey { /// /// # Example /// ``` - /// # use matrix_qrcode::{QrVerification, DecodingError}; + /// # use matrix_qrcode::{QrVerificationData, DecodingError}; /// # fn main() -> Result<(), DecodingError> { /// let data = b"MATRIX\ /// \x02\x02\x00\x06\ @@ -627,8 +627,8 @@ impl SelfVerificationNoMasterKey { /// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ /// SHARED_SECRET"; /// - /// let result = QrVerification::from_bytes(data)?; - /// if let QrVerification::SelfVerificationNoMasterKey(decoded) = result { + /// let result = QrVerificationData::from_bytes(data)?; + /// if let QrVerificationData::SelfVerificationNoMasterKey(decoded) = result { /// let encoded = decoded.to_bytes().unwrap(); /// assert_eq!(data.as_ref(), encoded.as_slice()); /// } else { @@ -665,7 +665,7 @@ impl SelfVerificationNoMasterKey { } } -impl From for QrVerification { +impl From for QrVerificationData { fn from(data: SelfVerificationNoMasterKey) -> Self { Self::SelfVerificationNoMasterKey(data) } From 71aba433da76f2bab5b491dde18ae8f8a3ef6432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 9 Jun 2021 17:40:31 +0200 Subject: [PATCH 07/88] crypto: Add some more accessors to the sas structs --- matrix_sdk/src/sas.rs | 23 ++++++++++++++++++- matrix_sdk_crypto/src/verification/mod.rs | 6 +++-- .../src/verification/sas/inner_sas.rs | 14 +++++++++++ matrix_sdk_crypto/src/verification/sas/mod.rs | 12 +++++++++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/matrix_sdk/src/sas.rs b/matrix_sdk/src/sas.rs index 42949feb..17db6a2f 100644 --- a/matrix_sdk/src/sas.rs +++ b/matrix_sdk/src/sas.rs @@ -15,6 +15,7 @@ use matrix_sdk_base::crypto::{ AcceptSettings, OutgoingVerificationRequest, ReadOnlyDevice, Sas as BaseSas, }; +use ruma::UserId; use crate::{error::Result, Client}; @@ -43,14 +44,19 @@ impl Sas { /// # use matrix_sdk::Client; /// # use futures::executor::block_on; /// # use url::Url; + /// # use ruma::identifiers::user_id; /// use matrix_sdk::Sas; /// use matrix_sdk_base::crypto::AcceptSettings; /// use matrix_sdk::events::key::verification::ShortAuthenticationString; /// # let homeserver = Url::parse("http://example.com").unwrap(); /// # let client = Client::new(homeserver).unwrap(); /// # let flow_id = "someID"; + /// # let user_id = user_id!("@alice:example"); /// # block_on(async { - /// let sas = client.get_verification(flow_id).await.unwrap(); + /// let sas = client + /// .get_verification(&user_id, flow_id) + /// .await + /// .unwrap(); /// /// let only_decimal = AcceptSettings::with_allowed_methods( /// vec![ShortAuthenticationString::Decimal] @@ -141,4 +147,19 @@ impl Sas { pub fn other_device(&self) -> &ReadOnlyDevice { self.inner.other_device() } + + /// Did this verification flow start from a verification request. + pub fn started_from_request(&self) -> bool { + self.inner.started_from_request() + } + + /// Is this a verification that is veryfying one of our own devices. + pub fn is_self_verification(&self) -> bool { + self.inner.is_self_verification() + } + + /// Get our own user id. + pub fn own_user_id(&self) -> &UserId { + self.inner.user_id() + } } diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 95040dff..f0c5776e 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -240,6 +240,10 @@ impl IdentitiesBeingVerified { self.private_identity.user_id() } + fn is_self_verification(&self) -> bool { + self.user_id() == self.other_user_id() + } + fn other_user_id(&self) -> &UserId { self.device_being_verified.user_id() } @@ -370,8 +374,6 @@ impl IdentitiesBeingVerified { return Ok(None); } - // TODO signal an error, e.g. when the identity got deleted so we don't - // verify/save the device either. let identity = self.store.get_user_identity(self.other_user_id()).await?; if let Some(identity) = identity { diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index b9b37475..67b5302e 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -61,6 +61,20 @@ impl InnerSas { (InnerSas::Created(sas), content.into()) } + pub fn started_from_request(&self) -> bool { + match self { + InnerSas::Created(s) => s.started_from_request, + InnerSas::Started(s) => s.started_from_request, + InnerSas::Accepted(s) => s.started_from_request, + InnerSas::KeyReceived(s) => s.started_from_request, + InnerSas::Confirmed(s) => s.started_from_request, + InnerSas::MacReceived(s) => s.started_from_request, + InnerSas::WaitingForDone(s) => s.started_from_request, + InnerSas::Done(s) => s.started_from_request, + InnerSas::Cancelled(s) => s.started_from_request, + } + } + pub fn supports_emoji(&self) -> bool { match self { InnerSas::Created(_) => false, diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index d8b0330f..46487ef1 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -49,8 +49,8 @@ use crate::{ ReadOnlyAccount, ToDeviceRequest, }; -#[derive(Clone, Debug)] /// Short authentication string object. +#[derive(Clone, Debug)] pub struct Sas { inner: Arc>, account: ReadOnlyAccount, @@ -95,6 +95,16 @@ impl Sas { self.inner.lock().unwrap().supports_emoji() } + /// Did this verification flow start from a verification request. + pub fn started_from_request(&self) -> bool { + self.inner.lock().unwrap().started_from_request() + } + + /// Is this a verification that is veryfying one of our own devices. + pub fn is_self_verification(&self) -> bool { + self.identities_being_verified.is_self_verification() + } + #[cfg(test)] #[allow(dead_code)] pub(crate) fn set_creation_time(&self, time: Instant) { From 00c3921d2a7456d70c68a2889b629254d88aa115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Wed, 9 Jun 2021 16:30:15 +0200 Subject: [PATCH 08/88] crypto: Add initial support for QR code verification --- matrix_sdk_crypto/Cargo.toml | 1 + matrix_sdk_crypto/src/identities/user.rs | 10 +- matrix_sdk_crypto/src/lib.rs | 3 +- matrix_sdk_crypto/src/machine.rs | 2 +- matrix_sdk_crypto/src/olm/signing/mod.rs | 21 +- .../src/session_manager/group_sessions.rs | 5 - matrix_sdk_crypto/src/store/mod.rs | 2 +- matrix_sdk_crypto/src/verification/cache.rs | 18 +- matrix_sdk_crypto/src/verification/machine.rs | 11 + matrix_sdk_crypto/src/verification/mod.rs | 52 +- matrix_sdk_crypto/src/verification/qrcode.rs | 886 ++++++++++++++++++ .../src/verification/requests.rs | 196 +++- 12 files changed, 1177 insertions(+), 30 deletions(-) create mode 100644 matrix_sdk_crypto/src/verification/qrcode.rs diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index 764b1f2c..bbad56e7 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -20,6 +20,7 @@ sled_cryptostore = ["sled"] docs = ["sled_cryptostore"] [dependencies] +matrix-qrcode = { version = "0.1.0", path = "../matrix_qrcode" } matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"] } diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index 4142ea0f..2015e56c 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -213,6 +213,14 @@ impl MasterPubkey { self.0.keys.get(key_id.as_str()).map(|k| k.as_str()) } + /// Get the first available master key. + /// + /// There's usually only a single master key so this will usually fetch the + /// only key. + pub fn get_first_key(&self) -> Option<&str> { + self.0.keys.values().map(|k| k.as_str()).next() + } + /// Check if the given cross signing sub-key is signed by the master key. /// /// # Arguments @@ -803,7 +811,7 @@ pub(crate) mod test { Arc::new(MemoryStore::new()), ); - let public_identity = identity.as_public_identity().await.unwrap(); + let public_identity = identity.to_public_identity().await.unwrap(); let mut device = Device { inner: device, diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index 1fef664e..dcd4db02 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -48,6 +48,7 @@ pub use identities::{ Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, UserDevices, UserIdentities, UserIdentity, }; pub use machine::OlmMachine; +pub use matrix_qrcode; pub use olm::EncryptionSettings; pub(crate) use olm::ReadOnlyAccount; pub use requests::{ @@ -55,4 +56,4 @@ pub use requests::{ OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest, }; pub use store::CryptoStoreError; -pub use verification::{AcceptSettings, Sas, VerificationRequest}; +pub use verification::{AcceptSettings, QrVerification, Sas, Verification, VerificationRequest}; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 6abde4de..258c61b1 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -379,7 +379,7 @@ impl OlmMachine { *identity = id; - let public = identity.as_public_identity().await.expect( + let public = identity.to_public_identity().await.expect( "Couldn't create a public version of the identity from a new private identity", ); diff --git a/matrix_sdk_crypto/src/olm/signing/mod.rs b/matrix_sdk_crypto/src/olm/signing/mod.rs index 7f4581cd..3f28b6ad 100644 --- a/matrix_sdk_crypto/src/olm/signing/mod.rs +++ b/matrix_sdk_crypto/src/olm/signing/mod.rs @@ -33,8 +33,8 @@ use serde::{Deserialize, Serialize}; use serde_json::Error as JsonError; use crate::{ - error::SignatureError, requests::UploadSigningKeysRequest, OwnUserIdentity, ReadOnlyAccount, - ReadOnlyDevice, UserIdentity, + error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest, + OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice, UserIdentity, }; /// Private cross signing identity. @@ -91,6 +91,21 @@ impl PrivateCrossSigningIdentity { !(has_master && has_user && has_self) } + /// Can we sign our own devices, i.e. do we have a self signing key. + pub async fn can_sign_devices(&self) -> bool { + self.self_signing_key.lock().await.is_some() + } + + /// Do we have the master key. + pub async fn has_master_key(&self) -> bool { + self.master_key.lock().await.is_some() + } + + /// Get the public part of the master key, if we have one. + pub async fn master_public_key(&self) -> Option { + self.master_key.lock().await.as_ref().map(|m| m.public_key.to_owned()) + } + /// Create a new empty identity. pub(crate) fn empty(user_id: UserId) -> Self { Self { @@ -102,7 +117,7 @@ impl PrivateCrossSigningIdentity { } } - pub(crate) async fn as_public_identity(&self) -> Result { + pub(crate) async fn to_public_identity(&self) -> Result { let master = self .master_key .lock() diff --git a/matrix_sdk_crypto/src/session_manager/group_sessions.rs b/matrix_sdk_crypto/src/session_manager/group_sessions.rs index 26269716..5d084a4c 100644 --- a/matrix_sdk_crypto/src/session_manager/group_sessions.rs +++ b/matrix_sdk_crypto/src/session_manager/group_sessions.rs @@ -146,11 +146,6 @@ impl GroupSessionManager { let mut changes = Changes::default(); changes.outbound_group_sessions.push(s.clone()); self.store.save_changes(changes).await?; - } else { - trace!( - request_id = request_id.to_string().as_str(), - "Marking room key share request as sent but session found that owns the given id" - ) } Ok(()) diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 1f41609d..022c5c03 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -239,7 +239,7 @@ impl Deref for Store { } } -#[derive(Error, Debug)] +#[derive(Debug, Error)] /// The crypto store's error type. pub enum CryptoStoreError { /// The account that owns the sessions, group sessions, and devices wasn't diff --git a/matrix_sdk_crypto/src/verification/cache.rs b/matrix_sdk_crypto/src/verification/cache.rs index 8d54a165..bf0fd9f8 100644 --- a/matrix_sdk_crypto/src/verification/cache.rs +++ b/matrix_sdk_crypto/src/verification/cache.rs @@ -19,7 +19,7 @@ use matrix_sdk_common::uuid::Uuid; use ruma::{DeviceId, UserId}; use super::{event_enums::OutgoingContent, sas::content_to_request, Sas, Verification}; -use crate::{OutgoingRequest, RoomMessageRequest}; +use crate::{OutgoingRequest, QrVerification, RoomMessageRequest}; #[derive(Clone, Debug)] pub struct VerificationCache { @@ -51,6 +51,20 @@ impl VerificationCache { self.insert(sas); } + pub fn insert_qr(&self, qr: QrVerification) { + self.insert(qr) + } + + pub fn get_qr(&self, sender: &UserId, flow_id: &str) -> Option { + self.get(sender, flow_id).and_then(|v| { + if let Verification::QrV1(qr) = v { + Some(qr) + } else { + None + } + }) + } + pub fn get(&self, sender: &UserId, flow_id: &str) -> Option { self.verification.get(sender).and_then(|m| m.get(flow_id).map(|v| v.clone())) } @@ -92,7 +106,7 @@ impl VerificationCache { pub fn get_sas(&self, user_id: &UserId, flow_id: &str) -> Option { self.get(user_id, flow_id).and_then(|v| { if let Verification::SasV1(sas) = v { - Some(sas.clone()) + Some(sas) } else { None } diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index c84e39dc..99c35ab7 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -340,6 +340,17 @@ impl VerificationMachine { self.mark_sas_as_done(sas, content).await?; } } + Some(Verification::QrV1(qr)) => { + let (cancellation, request) = qr.receive_done(&c).await?; + + if let Some(c) = cancellation { + self.verifications.add_request(c.into()) + } + + if let Some(s) = request { + self.verifications.add_request(s.into()) + } + } None => (), } } diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index f0c5776e..aa0a99e8 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(missing_docs)] - mod cache; mod event_enums; mod machine; +mod qrcode; mod requests; mod sas; @@ -24,6 +23,7 @@ use std::sync::Arc; use event_enums::OutgoingContent; pub use machine::VerificationMachine; +pub use qrcode::QrVerification; pub use requests::VerificationRequest; use ruma::{ api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest, @@ -47,18 +47,25 @@ use crate::{ CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities, }; +/// An enum over the different verification types the SDK supports. #[derive(Clone, Debug)] pub enum Verification { + /// The `m.sas.v1` verification variant. SasV1(Sas), + /// The `m.qr_code.*.v1` verification variant. + QrV1(QrVerification), } impl Verification { + /// Has this verification finished. pub fn is_done(&self) -> bool { match self { Verification::SasV1(s) => s.is_done(), + Verification::QrV1(qr) => qr.is_done(), } } + /// Try to deconstruct this verification enum into a SAS verification. pub fn sas_v1(self) -> Option { if let Verification::SasV1(sas) = self { Some(sas) @@ -67,21 +74,52 @@ impl Verification { } } + /// Try to deconstruct this verification enum into a QR code verification. + pub fn qr_v1(self) -> Option { + if let Verification::QrV1(qr) = self { + Some(qr) + } else { + None + } + } + + /// Get the ID that uniquely identifies this verification flow. pub fn flow_id(&self) -> &str { match self { Verification::SasV1(s) => s.flow_id().as_str(), + Verification::QrV1(qr) => qr.flow_id().as_str(), } } + /// Has the verification been cancelled. pub fn is_cancelled(&self) -> bool { match self { Verification::SasV1(s) => s.is_cancelled(), + Verification::QrV1(qr) => qr.is_cancelled(), } } + /// Get our own user id that is participating in this verification. + pub fn user_id(&self) -> &UserId { + match self { + Verification::SasV1(v) => v.user_id(), + Verification::QrV1(v) => v.user_id(), + } + } + + /// Get the other user id that is participating in this verification. pub fn other_user(&self) -> &UserId { match self { Verification::SasV1(s) => s.other_user_id(), + Verification::QrV1(qr) => qr.other_user_id(), + } + } + + /// Is this a verification verifying a device that belongs to us. + pub fn is_self_verification(&self) -> bool { + match self { + Verification::SasV1(v) => v.is_self_verification(), + Verification::QrV1(v) => v.is_self_verification(), } } } @@ -92,6 +130,12 @@ impl From for Verification { } } +impl From for Verification { + fn from(qr: QrVerification) -> Self { + Self::QrV1(qr) + } +} + /// The verification state indicating that the verification finished /// successfully. /// @@ -236,6 +280,10 @@ pub struct IdentitiesBeingVerified { } impl IdentitiesBeingVerified { + async fn can_sign_devices(&self) -> bool { + self.private_identity.can_sign_devices().await + } + fn user_id(&self) -> &UserId { self.private_identity.user_id() } diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs new file mode 100644 index 00000000..b9005b5f --- /dev/null +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -0,0 +1,886 @@ +// Copyright 2021 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::{Arc, Mutex}; + +use matrix_qrcode::{ + qrcode::QrCode, EncodingError, QrVerificationData, SelfVerificationData, + SelfVerificationNoMasterKey, VerificationData, +}; +use matrix_sdk_common::uuid::Uuid; +use ruma::{ + api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest, + events::{ + key::verification::{ + cancel::CancelCode, + done::{DoneEventContent, DoneToDeviceEventContent}, + start::{ + self, ReciprocateV1Content, StartEventContent, StartMethod, + StartToDeviceEventContent, + }, + Relation, + }, + AnyMessageEventContent, AnyToDeviceEventContent, + }, + identifiers::{DeviceIdBox, DeviceKeyAlgorithm, UserId}, +}; +use thiserror::Error; + +use super::{ + event_enums::{DoneContent, OutgoingContent, OwnedStartContent, StartContent}, + sas::content_to_request, + Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult, +}; +use crate::{ + olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, + store::CryptoStore, + CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, + UserIdentities, +}; + +const SECRET_SIZE: usize = 16; + +/// An error for the different failure modes that can happen during the +/// validation of a scanned QR code. +#[derive(Debug, Error)] +pub enum ScanError { + /// An IO error inside the crypto store happened during the validation of + /// the QR code scan. + #[error(transparent)] + Store(#[from] CryptoStoreError), + /// A key mismatch happened during the validation of the QR code scan. + #[error("The keys that are being verified didn't match (expected {expected}, found {found})")] + KeyMismatch { expected: String, found: String }, + /// One of the users that is participating in this verification doesn't have + /// a valid cross signing identity. + #[error("The user {0} is missing a valid cross signing identity")] + MissingCrossSigningIdentity(UserId), + /// The device of the user that is participating in this verification + /// doesn't have a valid device key. + #[error("The user's {0} device {1} is not E2E capable")] + MissingDeviceKeys(UserId, DeviceIdBox), + /// The ID uniquely identifying this verification flow didn't match to the + /// one that has been scanned. + #[error("The unique verification flow id did not match (expected {expected}, found {found})")] + FlowIdMismatch { expected: String, found: String }, +} + +/// An object controlling QR code style key verification flows. +#[derive(Clone)] +pub struct QrVerification { + flow_id: FlowId, + store: Arc, + inner: Arc, + state: Arc>, + identities: IdentitiesBeingVerified, +} + +impl std::fmt::Debug for QrVerification { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("QrVerification") + .field("flow_id", &self.flow_id) + .field("inner", self.inner.as_ref()) + .field("state", &self.state.lock().unwrap()) + .finish() + } +} + +impl QrVerification { + /// Has the QR verification been scanned by the other side. + /// + /// When the verification object is in this state it's required that the + /// user confirms that the other side has scanned the QR code. + pub fn is_scanned(&self) -> bool { + matches!(&*self.state.lock().unwrap(), InnerState::Scanned(_)) + } + + /// Get our own user id. + pub fn user_id(&self) -> &UserId { + self.identities.user_id() + } + + /// Get the user id of the other user that is participating in this + /// verification flow. + pub fn other_user_id(&self) -> &UserId { + self.identities.other_user_id() + } + + /// Has the verification flow completed. + pub fn is_done(&self) -> bool { + matches!(&*self.state.lock().unwrap(), InnerState::Done(_)) + } + + /// Has the verification flow been cancelled. + pub fn is_cancelled(&self) -> bool { + matches!(&*self.state.lock().unwrap(), InnerState::Cancelled(_)) + } + + /// Is this a verification that is veryfying one of our own devices + pub fn is_self_verification(&self) -> bool { + self.identities.is_self_verification() + } + + /// Get the unique ID that identifies this QR code verification flow. + pub fn flow_id(&self) -> &FlowId { + &self.flow_id + } + + /// Generate a QR code object that is representing this verification flow. + /// + /// The `QrCode` can then be rendered as an image or as an unicode string. + /// + /// The [`to_bytes()`](#method.to_bytes) method can be used to instead + /// output the raw bytes that should be encoded as a QR code. + pub fn to_qr_code(&self) -> Result { + self.inner.to_qr_code() + } + + /// Generate a the raw bytes that should be encoded as a QR code is + /// representing this verification flow. + /// + /// The [`to_qr_code()`](#method.to_qr_code) method can be used to instead + /// output a `QrCode` object that can be rendered. + pub fn to_bytes(&self) -> Result, EncodingError> { + self.inner.to_bytes() + } + + /// Cancel the verification flow. + pub fn cancel(&self) -> Option { + self.cancel_with_code(CancelCode::User).map(|c| self.content_to_request(c)) + } + + /// Notify the other side that we have successfully scanned the QR code and + /// that the QR verification flow can start. + /// + /// This will return some `OutgoingContent` if the object is in the correct + /// state to start the verification flow, otherwise `None`. + pub fn reciprocate(&self) -> Option { + match &*self.state.lock().unwrap() { + InnerState::Reciprocated(s) => Some(s.as_content(self.flow_id())), + InnerState::Created(_) + | InnerState::Scanned(_) + | InnerState::Confirmed(_) + | InnerState::Done(_) + | InnerState::Cancelled(_) => None, + } + } + + /// Confirm that the other side has scanned our QR code. + pub fn confirm_scanning(&self) -> Option { + let mut state = self.state.lock().unwrap(); + + match &*state { + InnerState::Scanned(s) => { + let new_state = s.clone().confirm_scanning(); + let content = new_state.as_content(&self.flow_id); + *state = InnerState::Confirmed(new_state); + + Some(self.content_to_request(content)) + } + InnerState::Created(_) + | InnerState::Cancelled(_) + | InnerState::Confirmed(_) + | InnerState::Reciprocated(_) + | InnerState::Done(_) => None, + } + } + + fn content_to_request(&self, content: OutgoingContent) -> OutgoingVerificationRequest { + match content { + OutgoingContent::Room(room_id, content) => { + RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into() + } + OutgoingContent::ToDevice(c) => content_to_request( + self.identities.other_user_id(), + self.identities.other_device_id().to_owned(), + c, + ) + .into(), + } + } + + fn cancel_with_code(&self, code: CancelCode) -> Option { + let new_state = QrState::::new(code); + let content = new_state.as_content(self.flow_id()); + + let mut state = self.state.lock().unwrap(); + + match &*state { + InnerState::Confirmed(_) + | InnerState::Created(_) + | InnerState::Scanned(_) + | InnerState::Reciprocated(_) + | InnerState::Done(_) => { + *state = InnerState::Cancelled(new_state); + Some(content) + } + InnerState::Cancelled(_) => None, + } + } + + async fn mark_as_done( + &self, + new_state: QrState, + ) -> Result< + (Option, Option), + CryptoStoreError, + > { + let (devices, identities) = new_state.verified_identities(); + + let mut new_state = InnerState::Done(new_state); + + let (content, request) = + match self.identities.mark_as_done(Some(&devices), Some(&identities)).await? { + VerificationResult::Ok => (None, None), + VerificationResult::Cancel(c) => { + let canceled = QrState::::new(c); + let content = canceled.as_content(self.flow_id()); + new_state = InnerState::Cancelled(canceled); + (Some(content), None) + } + VerificationResult::SignatureUpload(s) => (None, Some(s)), + }; + + *self.state.lock().unwrap() = new_state; + + Ok((content.map(|c| self.content_to_request(c)), request)) + } + + pub(crate) async fn receive_done( + &self, + content: &DoneContent<'_>, + ) -> Result< + (Option, Option), + CryptoStoreError, + > { + let state = (*self.state.lock().unwrap()).clone(); + + Ok(match state { + InnerState::Confirmed(s) => { + let (verified_device, verified_identity) = match &*self.inner { + QrVerificationData::Verification(_) => { + (None, self.identities.identity_being_verified.as_ref()) + } + QrVerificationData::SelfVerification(_) => { + (Some(&self.identities.device_being_verified), None) + } + QrVerificationData::SelfVerificationNoMasterKey(_) => { + (None, self.identities.identity_being_verified.as_ref()) + } + }; + + let new_state = s.clone().into_done(content, verified_device, verified_identity); + self.mark_as_done(new_state).await? + } + InnerState::Reciprocated(s) => { + let (verified_device, verified_identity) = match &*self.inner { + QrVerificationData::Verification(_) => { + (None, self.identities.identity_being_verified.as_ref()) + } + QrVerificationData::SelfVerification(_) => { + (None, self.identities.identity_being_verified.as_ref()) + } + QrVerificationData::SelfVerificationNoMasterKey(_) => { + (Some(&self.identities.device_being_verified), None) + } + }; + + let new_state = s.clone().into_done(content, verified_device, verified_identity); + let content = Some(new_state.as_content(self.flow_id())); + let (cancel_content, request) = self.mark_as_done(new_state).await?; + + if cancel_content.is_some() { + (cancel_content, request) + } else { + (content.map(|c| self.content_to_request(c)), request) + } + } + InnerState::Created(_) + | InnerState::Scanned(_) + | InnerState::Done(_) + | InnerState::Cancelled(_) => (None, None), + }) + } + + pub(crate) fn receive_reciprocation( + &self, + content: &StartContent, + ) -> Option { + let mut state = self.state.lock().unwrap(); + + match &*state { + InnerState::Created(s) => match s.clone().receive_reciprocate(content) { + Ok(s) => { + *state = InnerState::Scanned(s); + None + } + Err(s) => { + let content = s.as_content(self.flow_id()); + *state = InnerState::Cancelled(s); + Some(self.content_to_request(content)) + } + }, + InnerState::Confirmed(_) + | InnerState::Scanned(_) + | InnerState::Reciprocated(_) + | InnerState::Done(_) + | InnerState::Cancelled(_) => None, + } + } + + fn generate_secret() -> String { + let mut shared_secret = [0u8; SECRET_SIZE]; + getrandom::getrandom(&mut shared_secret) + .expect("Can't generate randomness for the shared secret"); + crate::utilities::encode(shared_secret) + } + + pub(crate) fn new_self( + store: Arc, + flow_id: FlowId, + own_master_key: String, + other_device_key: String, + identities: IdentitiesBeingVerified, + ) -> Self { + let secret = Self::generate_secret(); + + let inner: QrVerificationData = SelfVerificationData::new( + flow_id.as_str().to_owned(), + own_master_key, + other_device_key, + secret, + ) + .into(); + + Self::new_helper(store, flow_id, inner, identities) + } + + pub(crate) fn new_self_no_master( + account: ReadOnlyAccount, + store: Arc, + flow_id: FlowId, + own_master_key: String, + identities: IdentitiesBeingVerified, + ) -> QrVerification { + let secret = Self::generate_secret(); + + let inner: QrVerificationData = SelfVerificationNoMasterKey::new( + flow_id.as_str().to_owned(), + account.identity_keys().ed25519().to_string(), + own_master_key, + secret, + ) + .into(); + + Self::new_helper(store, flow_id, inner, identities) + } + + pub(crate) fn new_cross( + store: Arc, + flow_id: FlowId, + own_master_key: String, + other_master_key: String, + identities: IdentitiesBeingVerified, + ) -> Self { + let secret = Self::generate_secret(); + + let event_id = if let FlowId::InRoom(_, e) = &flow_id { + e.to_owned() + } else { + panic!("A verification between users is only valid in a room"); + }; + + let inner: QrVerificationData = + VerificationData::new(event_id, own_master_key, other_master_key, secret).into(); + + Self::new_helper(store, flow_id, inner, identities) + } + + pub(crate) async fn from_scan( + store: Arc, + own_account: ReadOnlyAccount, + private_identity: PrivateCrossSigningIdentity, + other_user_id: UserId, + other_device_id: DeviceIdBox, + flow_id: FlowId, + qr_code: QrVerificationData, + ) -> Result { + if flow_id.as_str() != qr_code.flow_id() { + return Err(ScanError::FlowIdMismatch { + expected: flow_id.as_str().to_owned(), + found: qr_code.flow_id().to_owned(), + }); + } + + let own_identity = + store.get_user_identity(own_account.user_id()).await?.ok_or_else(|| { + ScanError::MissingCrossSigningIdentity(own_account.user_id().to_owned()) + })?; + let other_identity = store + .get_user_identity(&other_user_id) + .await? + .ok_or_else(|| ScanError::MissingCrossSigningIdentity(other_user_id.clone()))?; + let other_device = + store.get_device(&other_user_id, &other_device_id).await?.ok_or_else(|| { + ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone()) + })?; + + let check_master_key = |key, identity: &UserIdentities| { + let master_key = identity.master_key().get_first_key().ok_or_else(|| { + ScanError::MissingCrossSigningIdentity(identity.user_id().clone()) + })?; + + if key != master_key { + Err(ScanError::KeyMismatch { + expected: master_key.to_owned(), + found: qr_code.first_key().to_owned(), + }) + } else { + Ok(()) + } + }; + + let identities = match qr_code { + QrVerificationData::Verification(_) => { + check_master_key(qr_code.first_key(), &other_identity)?; + check_master_key(qr_code.second_key(), &own_identity)?; + + IdentitiesBeingVerified { + private_identity, + store: store.clone(), + device_being_verified: other_device, + identity_being_verified: Some(other_identity), + } + } + QrVerificationData::SelfVerification(_) => { + check_master_key(qr_code.first_key(), &other_identity)?; + if qr_code.second_key() != own_account.identity_keys().ed25519() { + return Err(ScanError::KeyMismatch { + expected: own_account.identity_keys().ed25519().to_owned(), + found: qr_code.second_key().to_owned(), + }); + } + + IdentitiesBeingVerified { + private_identity, + store: store.clone(), + device_being_verified: other_device, + identity_being_verified: Some(other_identity), + } + } + QrVerificationData::SelfVerificationNoMasterKey(_) => { + let device_key = + other_device.get_key(DeviceKeyAlgorithm::Ed25519).ok_or_else(|| { + ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone()) + })?; + if qr_code.first_key() != device_key { + return Err(ScanError::KeyMismatch { + expected: device_key.to_owned(), + found: qr_code.first_key().to_owned(), + }); + } + check_master_key(qr_code.second_key(), &other_identity)?; + IdentitiesBeingVerified { + private_identity, + store: store.clone(), + device_being_verified: other_device, + identity_being_verified: None, + } + } + }; + + let secret = qr_code.secret().to_owned(); + + Ok(Self { + store, + flow_id, + inner: qr_code.into(), + state: Mutex::new(InnerState::Reciprocated(QrState { + state: Reciprocated { secret, own_device_id: own_account.device_id().to_owned() }, + })) + .into(), + identities, + }) + } + + fn new_helper( + store: Arc, + flow_id: FlowId, + inner: QrVerificationData, + identities: IdentitiesBeingVerified, + ) -> Self { + let secret = inner.secret().to_owned(); + + Self { + store, + flow_id, + inner: inner.into(), + state: Mutex::new(InnerState::Created(QrState { state: Created { secret } })).into(), + identities, + } + } +} + +#[derive(Debug, Clone)] +enum InnerState { + Created(QrState), + Scanned(QrState), + Confirmed(QrState), + Reciprocated(QrState), + Done(QrState), + Cancelled(QrState), +} + +#[derive(Clone, Debug)] +struct QrState { + state: S, +} + +#[derive(Clone, Debug)] +struct Created { + secret: String, +} + +#[derive(Clone, Debug)] +struct Scanned {} + +#[derive(Clone, Debug)] +struct Confirmed {} + +#[derive(Clone, Debug)] +struct Reciprocated { + own_device_id: DeviceIdBox, + secret: String, +} + +impl Reciprocated { + fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { + let content = ReciprocateV1Content::new(self.secret.clone()); + let method = StartMethod::ReciprocateV1(content); + + let content: OwnedStartContent = match flow_id { + FlowId::ToDevice(t) => { + StartToDeviceEventContent::new(self.own_device_id.clone(), t.clone(), method).into() + } + FlowId::InRoom(r, e) => ( + r.clone(), + StartEventContent::new( + self.own_device_id.clone(), + method, + Relation::new(e.clone()), + ), + ) + .into(), + }; + + content.into() + } +} + +impl QrState { + fn confirm_scanning(self) -> QrState { + QrState { state: Confirmed {} } + } +} + +impl QrState { + fn new(cancel_code: CancelCode) -> Self { + QrState { state: Cancelled::new(cancel_code) } + } + + fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { + self.state.as_content(flow_id) + } +} + +impl QrState { + fn receive_reciprocate( + self, + content: &StartContent, + ) -> Result, QrState> { + match content.method() { + start::StartMethod::ReciprocateV1(m) => { + // TODO use constant time eq here. + if self.state.secret == m.secret { + Ok(QrState { state: Scanned {} }) + } else { + Err(QrState::::new(CancelCode::KeyMismatch)) + } + } + _ => Err(QrState::::new(CancelCode::UnknownMethod)), + } + } +} + +impl QrState { + fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { + self.state.as_content(flow_id) + } + + fn verified_identities(&self) -> (Arc<[ReadOnlyDevice]>, Arc<[UserIdentities]>) { + (self.state.verified_devices.clone(), self.state.verified_master_keys.clone()) + } +} + +impl QrState { + fn into_done( + self, + _: &DoneContent, + verified_device: Option<&ReadOnlyDevice>, + verified_identity: Option<&UserIdentities>, + ) -> QrState { + let devices: Vec<_> = verified_device.into_iter().cloned().collect(); + let identities: Vec<_> = verified_identity.into_iter().cloned().collect(); + + QrState { + state: Done { + verified_devices: devices.into(), + verified_master_keys: identities.into(), + }, + } + } + + fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { + match flow_id { + FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone( + DoneToDeviceEventContent::new(t.to_owned()), + ) + .into(), + FlowId::InRoom(r, e) => ( + r.to_owned(), + AnyMessageEventContent::KeyVerificationDone(DoneEventContent::new(Relation::new( + e.to_owned(), + ))), + ) + .into(), + } + } +} + +impl QrState { + fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { + self.state.as_content(flow_id) + } + + fn into_done( + self, + _: &DoneContent, + verified_device: Option<&ReadOnlyDevice>, + verified_identity: Option<&UserIdentities>, + ) -> QrState { + let devices: Vec<_> = verified_device.into_iter().cloned().collect(); + let identities: Vec<_> = verified_identity.into_iter().cloned().collect(); + + QrState { + state: Done { + verified_devices: devices.into(), + verified_master_keys: identities.into(), + }, + } + } +} + +#[cfg(test)] +mod test { + use std::{convert::TryFrom, sync::Arc}; + + use matrix_qrcode::QrVerificationData; + use matrix_sdk_test::async_test; + use ruma::identifiers::{event_id, room_id, user_id, DeviceIdBox, UserId}; + + use crate::{ + olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, + store::{Changes, CryptoStore, MemoryStore}, + verification::{ + event_enums::{DoneContent, OutgoingContent, StartContent}, + FlowId, IdentitiesBeingVerified, + }, + QrVerification, ReadOnlyDevice, + }; + + fn user_id() -> UserId { + user_id!("@example:localhost") + } + + fn memory_store() -> Arc { + Arc::new(MemoryStore::new()) + } + + fn device_id() -> DeviceIdBox { + "DEVICEID".into() + } + + #[async_test] + async fn test_verification_creation() { + let store = memory_store(); + + let account = ReadOnlyAccount::new(&user_id(), &device_id()); + let private_identity = PrivateCrossSigningIdentity::new(user_id()).await; + let flow_id = FlowId::ToDevice("test_transaction".to_owned()); + + let device_key = account.identity_keys().ed25519().to_owned(); + let master_key = private_identity.master_public_key().await.unwrap(); + let master_key = master_key.get_first_key().unwrap().to_owned(); + + let alice_device = ReadOnlyDevice::from_account(&account).await; + + let identities = IdentitiesBeingVerified { + private_identity, + store: store.clone(), + device_being_verified: alice_device, + identity_being_verified: None, + }; + + let verification = QrVerification::new_self_no_master( + account, + store.clone(), + flow_id.clone(), + master_key.clone(), + identities.clone(), + ); + + assert_eq!(verification.inner.first_key(), &device_key); + assert_eq!(verification.inner.second_key(), &master_key); + + let verification = QrVerification::new_self( + store.clone(), + flow_id, + master_key.clone(), + device_key.clone(), + identities.clone(), + ); + + assert_eq!(verification.inner.first_key(), &master_key); + assert_eq!(verification.inner.second_key(), &device_key); + + let bob_identity = PrivateCrossSigningIdentity::new(user_id!("@bob:example")).await; + let bob_master_key = bob_identity.master_public_key().await.unwrap(); + let bob_master_key = bob_master_key.get_first_key().unwrap().to_owned(); + + let flow_id = FlowId::InRoom(room_id!("!test:example"), event_id!("$EVENTID")); + + let verification = QrVerification::new_cross( + store.clone(), + flow_id, + master_key.clone(), + bob_master_key.clone(), + identities, + ); + + assert_eq!(verification.inner.first_key(), &master_key); + assert_eq!(verification.inner.second_key(), &bob_master_key); + } + + #[async_test] + async fn test_reciprocate_receival() { + let test = |flow_id: FlowId| async move { + let alice_account = ReadOnlyAccount::new(&user_id(), &device_id()); + let store = memory_store(); + + let bob_account = ReadOnlyAccount::new(alice_account.user_id(), "BOBDEVICE".into()); + + let private_identity = PrivateCrossSigningIdentity::new(user_id()).await; + let identity = private_identity.to_public_identity().await.unwrap(); + + let master_key = private_identity.master_public_key().await.unwrap(); + let master_key = master_key.get_first_key().unwrap().to_owned(); + + let alice_device = ReadOnlyDevice::from_account(&alice_account).await; + let bob_device = ReadOnlyDevice::from_account(&bob_account).await; + + let mut changes = Changes::default(); + changes.identities.new.push(identity.clone().into()); + changes.devices.new.push(bob_device.clone()); + store.save_changes(changes).await.unwrap(); + + let identities = IdentitiesBeingVerified { + private_identity: PrivateCrossSigningIdentity::empty( + alice_account.user_id().to_owned(), + ), + store: store.clone(), + device_being_verified: alice_device.clone(), + identity_being_verified: Some(identity.clone().into()), + }; + + let alice_verification = QrVerification::new_self_no_master( + alice_account.clone(), + store, + flow_id.clone(), + master_key.clone(), + identities, + ); + + let bob_store = memory_store(); + + let mut changes = Changes::default(); + changes.identities.new.push(identity.into()); + changes.devices.new.push(alice_device.clone()); + bob_store.save_changes(changes).await.unwrap(); + + let qr_code = alice_verification.to_bytes().unwrap(); + let qr_code = QrVerificationData::from_bytes(qr_code).unwrap(); + + let bob_verification = QrVerification::from_scan( + bob_store, + bob_account, + private_identity, + alice_account.user_id().to_owned(), + alice_account.device_id().to_owned(), + flow_id, + qr_code, + ) + .await + .unwrap(); + + let content = bob_verification.reciprocate().unwrap(); + let content = StartContent::try_from(&content).unwrap(); + + alice_verification.receive_reciprocation(&content); + + let request = alice_verification.confirm_scanning().unwrap(); + let content = OutgoingContent::from(request); + let content = DoneContent::try_from(&content).unwrap(); + + assert!(!alice_verification.is_done()); + assert!(!bob_verification.is_done()); + + let (request, _) = bob_verification.receive_done(&content).await.unwrap(); + let content = OutgoingContent::from(request.unwrap()); + let content = DoneContent::try_from(&content).unwrap(); + alice_verification.receive_done(&content).await.unwrap(); + + assert!(alice_verification.is_done()); + assert!(bob_verification.is_done()); + + let identity = alice_verification + .store + .get_user_identity(alice_account.user_id()) + .await + .unwrap() + .unwrap(); + + let identity = identity.own().unwrap(); + + assert!(!bob_device.is_trusted()); + assert!(alice_device.is_trusted()); + assert!(identity.is_verified()); + }; + + let flow_id = FlowId::ToDevice("test_transaction".to_owned()); + test(flow_id).await; + + let flow_id = FlowId::InRoom(room_id!("!test:example"), event_id!("$EVENTID")); + test(flow_id).await; + } +} diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 8e25cf3b..758b1d90 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -16,6 +16,7 @@ use std::sync::{Arc, Mutex}; +use matrix_qrcode::QrVerificationData; use matrix_sdk_common::uuid::Uuid; use ruma::{ api::client::r0::to_device::DeviceIdOrAllDevices, @@ -30,17 +31,18 @@ use ruma::{ room::message::KeyVerificationRequestEventContent, AnyMessageEventContent, AnyToDeviceEventContent, }, - DeviceId, DeviceIdBox, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; -use tracing::{info, warn}; +use tracing::{info, trace, warn}; use super::{ cache::VerificationCache, event_enums::{ CancelContent, DoneContent, OutgoingContent, ReadyContent, RequestContent, StartContent, }, + qrcode::{QrVerification, ScanError}, sas::content_to_request, - Cancelled, FlowId, + Cancelled, FlowId, IdentitiesBeingVerified, }; use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, @@ -49,10 +51,20 @@ use crate::{ ToDeviceRequest, UserIdentities, }; -const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1]; +const SUPPORTED_METHODS: &[VerificationMethod] = &[ + VerificationMethod::MSasV1, + VerificationMethod::MQrCodeShowV1, + VerificationMethod::MReciprocateV1, +]; +/// An object controlling key verification requests. +/// +/// Interactive verification flows usually start with a verification request, +/// this object lets you send and reply to such a verification request. +/// +/// After the initial handshake the verification flow transitions into one of +/// the verification methods. #[derive(Clone, Debug)] -/// TODO pub struct VerificationRequest { verification_cache: VerificationCache, account: ReadOnlyAccount, @@ -62,7 +74,6 @@ pub struct VerificationRequest { } impl VerificationRequest { - /// TODO pub(crate) fn new( cache: VerificationCache, account: ReadOnlyAccount, @@ -93,7 +104,6 @@ impl VerificationRequest { } } - /// TODO pub(crate) fn new_to_device( cache: VerificationCache, account: ReadOnlyAccount, @@ -122,7 +132,10 @@ impl VerificationRequest { } } - /// TODO + /// Create an event content that can be sent as a to-device event to request + /// verification from the other side. This should be used only for + /// self-verifications and it should be sent to the specific device that we + /// want to verify. pub fn request_to_device(&self) -> RequestToDeviceEventContent { RequestToDeviceEventContent::new( self.account.device_id().into(), @@ -132,7 +145,10 @@ impl VerificationRequest { ) } - /// TODO + /// Create an event content that can be sent as a room event to request + /// verification from the other side. This should be used only for + /// verifications of other users and it should be sent to a room we consider + /// to be a DM with the other user. pub fn request( own_user_id: &UserId, own_device_id: &DeviceId, @@ -157,11 +173,21 @@ impl VerificationRequest { &self.other_user_id } + /// Is the verification request ready to start a verification flow. + pub fn is_ready(&self) -> bool { + matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_)) + } + /// Get the unique ID of this verification request pub fn flow_id(&self) -> &FlowId { &self.flow_id } + /// Is this a verification that is veryfying one of our own devices + pub fn is_self_verification(&self) -> bool { + self.account.user_id() == self.other_user() + } + /// Has the verification flow that was started with this request finished. pub fn is_done(&self) -> bool { matches!(&*self.inner.lock().unwrap(), InnerRequest::Done(_)) @@ -173,6 +199,43 @@ impl VerificationRequest { matches!(&*self.inner.lock().unwrap(), InnerRequest::Cancelled(_)) } + /// Generate a QR code that can be used by another client to start a QR code + /// based verification. + pub async fn generate_qr_code(&self) -> Result, CryptoStoreError> { + self.inner.lock().unwrap().generate_qr_code().await + } + + /// Start a QR code verification by providing a scanned QR code for this + /// verification flow. + /// + /// Returns a `ScanError` if the QR code isn't valid, `None` if the + /// verification request isn't in the ready state or we don't support QR + /// code verification, otherwise a newly created `QrVerification` object + /// which will be used for the remainder of the verification flow. + pub async fn scan_qr_code( + &self, + data: QrVerificationData, + ) -> Result, ScanError> { + let state = self.inner.lock().unwrap(); + + if let InnerRequest::Ready(r) = &*state { + Ok(Some( + QrVerification::from_scan( + r.store.clone(), + r.account.clone(), + r.private_cross_signing_identity.clone(), + r.other_user_id.clone(), + r.state.other_device_id.clone(), + r.state.flow_id.clone(), + data, + ) + .await?, + )) + } else { + Ok(None) + } + } + pub(crate) fn from_request( cache: VerificationCache, account: ReadOnlyAccount, @@ -258,11 +321,6 @@ impl VerificationRequest { } } - /// Is the verification request ready to start a verification flow. - pub fn is_ready(&self) -> bool { - matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_)) - } - pub(crate) fn start( &self, device: ReadOnlyDevice, @@ -357,6 +415,17 @@ impl InnerRequest { }) } + async fn generate_qr_code(&self) -> Result, CryptoStoreError> { + match self { + InnerRequest::Created(_) => Ok(None), + InnerRequest::Requested(_) => Ok(None), + InnerRequest::Ready(s) => s.generate_qr_code().await, + InnerRequest::Passive(_) => Ok(None), + InnerRequest::Done(_) => Ok(None), + InnerRequest::Cancelled(_) => Ok(None), + } + } + fn to_started_sas( &self, content: &StartContent, @@ -570,6 +639,86 @@ impl RequestState { ) } + async fn generate_qr_code(&self) -> Result, CryptoStoreError> { + // TODO return an error explaining why we can't generate a QR code? + let device = if let Some(device) = + self.store.get_device(&self.other_user_id, &self.state.other_device_id).await? + { + device + } else { + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't create a QR code, the device that accepted the \ + verification doesn't exist" + ); + return Ok(None); + }; + + let identites = IdentitiesBeingVerified { + private_identity: self.private_cross_signing_identity.clone(), + store: self.store.clone(), + device_being_verified: device, + identity_being_verified: self.store.get_user_identity(&self.other_user_id).await?, + }; + + let verification = if let Some(identity) = &identites.identity_being_verified { + match &identity { + UserIdentities::Own(i) => { + if identites.can_sign_devices().await { + Some(QrVerification::new_self( + self.store.clone(), + self.flow_id.as_ref().to_owned(), + i.master_key().get_first_key().unwrap().to_owned(), + identites + .other_device() + .get_key(DeviceKeyAlgorithm::Ed25519) + .unwrap() + .to_owned(), + identites, + )) + } else { + Some(QrVerification::new_self_no_master( + self.account.clone(), + self.store.clone(), + self.flow_id.as_ref().to_owned(), + i.master_key().get_first_key().unwrap().to_owned(), + identites, + )) + } + } + UserIdentities::Other(i) => Some(QrVerification::new_cross( + self.store.clone(), + self.flow_id.as_ref().to_owned(), + self.private_cross_signing_identity + .master_public_key() + .await + .unwrap() + .get_first_key() + .unwrap() + .to_owned(), + i.master_key().get_first_key().unwrap().to_owned(), + identites, + )), + } + } else { + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't create a QR code, the user doesn't have a valid cross \ + signing identity." + ); + + None + }; + + if let Some(verification) = &verification { + self.verification_cache.insert_qr(verification.clone()); + } + + Ok(verification) + } + async fn receive_start( &self, sender: &UserId, @@ -597,6 +746,10 @@ impl RequestState { match content.method() { StartMethod::SasV1(_) => match self.to_started_sas(content, device.clone(), identity) { + // TODO check if there is already a SAS verification, i.e. we + // already started one before the other side tried to do the + // same; ignore it if we did and we're the lexicographically + // smaller user ID, otherwise auto-accept the newly started one. Ok(s) => { info!("Started a new SAS verification."); self.verification_cache.insert_sas(s); @@ -615,6 +768,21 @@ impl RequestState { ) } }, + StartMethod::ReciprocateV1(_) => { + if let Some(qr_verification) = + self.verification_cache.get_qr(sender, content.flow_id()) + { + if let Some(request) = qr_verification.receive_reciprocation(content) { + self.verification_cache.add_request(request.into()) + } + trace!( + sender = device.user_id().as_str(), + device_id = device.device_id().as_str(), + verification =? qr_verification, + "Received a QR code reciprocation" + ) + } + } m => { warn!(method =? m, "Received a key verification start event with an unsupported method") } From b14d754aedf1576aad7cc64bcb192e4275c35a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 10 Jun 2021 13:24:13 +0200 Subject: [PATCH 09/88] crypto: Turn the content_to_request function into a constructor Closes: #260 --- matrix_sdk_crypto/src/requests.rs | 44 ++++++++++++++++--- matrix_sdk_crypto/src/verification/cache.rs | 6 +-- matrix_sdk_crypto/src/verification/machine.rs | 5 ++- matrix_sdk_crypto/src/verification/qrcode.rs | 5 +-- .../src/verification/requests.rs | 11 +---- .../src/verification/sas/helpers.rs | 34 +------------- matrix_sdk_crypto/src/verification/sas/mod.rs | 3 +- 7 files changed, 51 insertions(+), 57 deletions(-) diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index fc3faa9b..318e8000 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(missing_docs)] - use std::{collections::BTreeMap, sync::Arc, time::Duration}; use matrix_sdk_common::uuid::Uuid; @@ -32,7 +30,7 @@ use ruma::{ message::send_message_event::Response as RoomMessageResponse, to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices}, }, - events::{AnyMessageEventContent, EventType}, + events::{AnyMessageEventContent, AnyToDeviceEventContent, EventContent, EventType}, DeviceIdBox, RoomId, UserId, }; use serde::{Deserialize, Serialize}; @@ -60,6 +58,35 @@ pub struct ToDeviceRequest { } impl ToDeviceRequest { + /// Create a new owned to-device request + /// + /// # Arguments + /// + /// * `recipient` - The ID of the user that should receive this to-device + /// event. + /// + /// * `recipient_device` - The device that should receive this to-device + /// event, or all devices. + /// + /// * `content` - The content of the to-device event. + pub(crate) fn new( + recipient: &UserId, + recipient_device: impl Into, + content: AnyToDeviceEventContent, + ) -> Self { + let mut messages = BTreeMap::new(); + let mut user_messages = BTreeMap::new(); + + user_messages.insert( + recipient_device.into(), + serde_json::value::to_raw_value(&content).expect("Can't serialize to-device content"), + ); + messages.insert(recipient.clone(), user_messages); + let event_type = EventType::from(content.event_type()); + + ToDeviceRequest { txn_id: Uuid::new_v4(), event_type, messages } + } + /// Gets the transaction ID as a string. pub fn txn_id_string(&self) -> String { self.txn_id.to_string() @@ -133,6 +160,8 @@ pub enum OutgoingRequests { /// Signature upload request, this request is used after a successful device /// or user verification is done. SignatureUpload(SignatureUploadRequest), + /// A room message request, usually for sending in-room interactive + /// verification events. RoomMessage(RoomMessageRequest), } @@ -205,9 +234,9 @@ pub enum IncomingResponse<'a> { /// The cross signing keys upload response, marking our private cross /// signing identity as shared. SigningKeysUpload(&'a SigningKeysUploadResponse), - /// The cross signing keys upload response, marking our private cross - /// signing identity as shared. + /// The cross signing signature upload response. SignatureUpload(&'a SignatureUploadResponse), + /// A room message response, usually for interactive verifications. RoomMessage(&'a RoomMessageResponse), } @@ -270,6 +299,7 @@ impl OutgoingRequest { } } +/// Customized owned request type for sending out room messages. #[derive(Clone, Debug)] pub struct RoomMessageRequest { /// The room to send the event to. @@ -286,13 +316,17 @@ pub struct RoomMessageRequest { pub content: AnyMessageEventContent, } +/// An enum over the different outgoing verification based requests. #[derive(Clone, Debug)] pub enum OutgoingVerificationRequest { + /// The to-device verification request variant. ToDevice(ToDeviceRequest), + /// The in-room verification request variant. InRoom(RoomMessageRequest), } impl OutgoingVerificationRequest { + /// Get the unique id of this request. pub fn request_id(&self) -> Uuid { match self { OutgoingVerificationRequest::ToDevice(t) => t.txn_id, diff --git a/matrix_sdk_crypto/src/verification/cache.rs b/matrix_sdk_crypto/src/verification/cache.rs index bf0fd9f8..5d029634 100644 --- a/matrix_sdk_crypto/src/verification/cache.rs +++ b/matrix_sdk_crypto/src/verification/cache.rs @@ -18,8 +18,8 @@ use dashmap::DashMap; use matrix_sdk_common::uuid::Uuid; use ruma::{DeviceId, UserId}; -use super::{event_enums::OutgoingContent, sas::content_to_request, Sas, Verification}; -use crate::{OutgoingRequest, QrVerification, RoomMessageRequest}; +use super::{event_enums::OutgoingContent, Sas, Verification}; +use crate::{OutgoingRequest, QrVerification, RoomMessageRequest, ToDeviceRequest}; #[derive(Clone, Debug)] pub struct VerificationCache { @@ -125,7 +125,7 @@ impl VerificationCache { ) { match content { OutgoingContent::ToDevice(c) => { - let request = content_to_request(recipient, recipient_device.to_owned(), c); + let request = ToDeviceRequest::new(recipient, recipient_device.to_owned(), c); let request_id = request.txn_id; let request = OutgoingRequest { request_id, request: Arc::new(request.into()) }; diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 99c35ab7..e9f7faf6 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -23,7 +23,7 @@ use super::{ cache::VerificationCache, event_enums::{AnyEvent, AnyVerificationContent, OutgoingContent}, requests::VerificationRequest, - sas::{content_to_request, Sas}, + sas::Sas, FlowId, Verification, VerificationResult, }; use crate::{ @@ -31,6 +31,7 @@ use crate::{ requests::OutgoingRequest, store::{CryptoStore, CryptoStoreError}, OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, RoomMessageRequest, + ToDeviceRequest, }; #[derive(Clone, Debug)] @@ -79,7 +80,7 @@ impl VerificationMachine { } OutgoingContent::ToDevice(c) => { let request = - content_to_request(device.user_id(), device.device_id().to_owned(), c); + ToDeviceRequest::new(device.user_id(), device.device_id().to_owned(), c); self.verifications.insert_sas(sas.clone()); diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index b9005b5f..003a75de 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -39,14 +39,13 @@ use thiserror::Error; use super::{ event_enums::{DoneContent, OutgoingContent, OwnedStartContent, StartContent}, - sas::content_to_request, Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, store::CryptoStore, CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, - UserIdentities, + ToDeviceRequest, UserIdentities, }; const SECRET_SIZE: usize = 16; @@ -201,7 +200,7 @@ impl QrVerification { OutgoingContent::Room(room_id, content) => { RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into() } - OutgoingContent::ToDevice(c) => content_to_request( + OutgoingContent::ToDevice(c) => ToDeviceRequest::new( self.identities.other_user_id(), self.identities.other_device_id().to_owned(), c, diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 758b1d90..aea0dde0 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -41,7 +41,6 @@ use super::{ CancelContent, DoneContent, OutgoingContent, ReadyContent, RequestContent, StartContent, }, qrcode::{QrVerification, ScanError}, - sas::content_to_request, Cancelled, FlowId, IdentitiesBeingVerified, }; use crate::{ @@ -268,7 +267,7 @@ impl VerificationRequest { inner.accept().map(|c| match c { OutgoingContent::ToDevice(content) => { - self.content_to_request(inner.other_device_id(), content).into() + ToDeviceRequest::new(&self.other_user(), inner.other_device_id(), content).into() } OutgoingContent::Room(room_id, content) => { RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into() @@ -337,14 +336,6 @@ impl VerificationRequest { _ => 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(Clone, Debug)] diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 4d4794e5..868df891 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -14,17 +14,15 @@ use std::{collections::BTreeMap, convert::TryInto}; -use matrix_sdk_common::uuid::Uuid; use olm_rs::sas::OlmSas; use ruma::{ - api::client::r0::to_device::DeviceIdOrAllDevices, events::{ key::verification::{ cancel::CancelCode, mac::{MacEventContent, MacToDeviceEventContent}, Relation, }, - AnyMessageEventContent, AnyToDeviceEventContent, EventType, + AnyMessageEventContent, AnyToDeviceEventContent, }, DeviceKeyAlgorithm, DeviceKeyId, UserId, }; @@ -36,7 +34,7 @@ use crate::{ identities::{ReadOnlyDevice, UserIdentities}, utilities::encode, verification::event_enums::{MacContent, StartContent}, - ReadOnlyAccount, ToDeviceRequest, + ReadOnlyAccount, }; #[derive(Clone, Debug)] @@ -527,34 +525,6 @@ fn bytes_to_decimal(bytes: Vec) -> (u16, u16, u16) { (first + 1000, second + 1000, third + 1000) } -pub fn content_to_request( - recipient: &UserId, - recipient_device: impl Into, - content: AnyToDeviceEventContent, -) -> ToDeviceRequest { - let mut messages = BTreeMap::new(); - let mut user_messages = BTreeMap::new(); - - user_messages.insert( - recipient_device.into(), - serde_json::value::to_raw_value(&content).expect("Can't serialize to-device content"), - ); - messages.insert(recipient.clone(), user_messages); - - let event_type = match content { - AnyToDeviceEventContent::KeyVerificationAccept(_) => EventType::KeyVerificationAccept, - AnyToDeviceEventContent::KeyVerificationStart(_) => EventType::KeyVerificationStart, - AnyToDeviceEventContent::KeyVerificationKey(_) => EventType::KeyVerificationKey, - AnyToDeviceEventContent::KeyVerificationMac(_) => EventType::KeyVerificationMac, - AnyToDeviceEventContent::KeyVerificationCancel(_) => EventType::KeyVerificationCancel, - AnyToDeviceEventContent::KeyVerificationReady(_) => EventType::KeyVerificationReady, - AnyToDeviceEventContent::KeyVerificationDone(_) => EventType::KeyVerificationDone, - _ => unreachable!(), - }; - - ToDeviceRequest { txn_id: Uuid::new_v4(), event_type, messages } -} - #[cfg(test)] mod test { use proptest::prelude::*; diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 46487ef1..4e50d89a 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -20,7 +20,6 @@ use std::sync::{Arc, Mutex}; #[cfg(test)] use std::time::Instant; -pub use helpers::content_to_request; use inner_sas::InnerSas; use matrix_sdk_common::uuid::Uuid; use ruma::{ @@ -442,7 +441,7 @@ impl Sas { } pub(crate) fn content_to_request(&self, content: AnyToDeviceEventContent) -> ToDeviceRequest { - content_to_request(self.other_user_id(), self.other_device_id().to_owned(), content) + ToDeviceRequest::new(self.other_user_id(), self.other_device_id().to_owned(), content) } } From cc0388929a632fd2495d1c208176a92d83b2b3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 10 Jun 2021 16:23:16 +0200 Subject: [PATCH 10/88] crypto: Add some more accessors for the fields in the verification types --- matrix_sdk_crypto/src/verification/mod.rs | 16 ++++++------ .../src/verification/requests.rs | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index aa0a99e8..0a8f789e 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -57,14 +57,6 @@ pub enum Verification { } impl Verification { - /// Has this verification finished. - pub fn is_done(&self) -> bool { - match self { - Verification::SasV1(s) => s.is_done(), - Verification::QrV1(qr) => qr.is_done(), - } - } - /// Try to deconstruct this verification enum into a SAS verification. pub fn sas_v1(self) -> Option { if let Verification::SasV1(sas) = self { @@ -83,6 +75,14 @@ impl Verification { } } + /// Has this verification finished. + pub fn is_done(&self) -> bool { + match self { + Verification::SasV1(s) => s.is_done(), + Verification::QrV1(qr) => qr.is_done(), + } + } + /// Get the ID that uniquely identifies this verification flow. pub fn flow_id(&self) -> &str { match self { diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index aea0dde0..363e4f29 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -166,6 +166,11 @@ impl VerificationRequest { ) } + /// Our own user id. + pub fn own_user_id(&self) -> &UserId { + self.account.user_id() + } + /// The id of the other user that is participating in this verification /// request. pub fn other_user(&self) -> &UserId { @@ -275,6 +280,27 @@ impl VerificationRequest { }) } + /// Cancel the verification request + pub fn cancel(&self) -> Option { + let mut inner = self.inner.lock().unwrap(); + inner.cancel(&CancelCode::User); + + let content = if let InnerRequest::Cancelled(c) = &*inner { + Some(c.state.as_content(self.flow_id())) + } else { + None + }; + + content.map(|c| match c { + OutgoingContent::ToDevice(content) => { + ToDeviceRequest::new(&self.other_user(), 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(&self, sender: &UserId, content: &ReadyContent) -> Result<(), ()> { let mut inner = self.inner.lock().unwrap(); From 073b91fa6224a9e13df1a8a768a134d2d8f2b946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:08:31 +0200 Subject: [PATCH 11/88] crypto: Ignore verification requests that are sent by us --- .../src/verification/event_enums.rs | 4 ++ matrix_sdk_crypto/src/verification/machine.rs | 37 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index 895e34b3..a8805d70 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -64,6 +64,10 @@ impl AnyEvent<'_> { } } + pub fn is_room_event(&self) -> bool { + matches!(self, AnyEvent::Room(_)) + } + pub fn verification_content(&self) -> Option { match self { AnyEvent::Room(e) => match e { diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index e9f7faf6..8278fc52 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -204,6 +204,14 @@ impl VerificationMachine { ); }; + let event_sent_from_us = |event: &AnyEvent<'_>, from_device: &DeviceId| { + if event.sender() == self.account.user_id() { + from_device == self.account.device_id() || event.is_room_event() + } else { + false + } + }; + if let Some(content) = event.verification_content() { match &content { AnyVerificationContent::Request(r) => { @@ -215,17 +223,26 @@ impl VerificationMachine { if let Some(timestamp) = event.timestamp() { if Self::is_timestamp_valid(timestamp) { - let request = VerificationRequest::from_request( - self.verifications.clone(), - self.account.clone(), - self.private_identity.lock().await.clone(), - self.store.clone(), - event.sender(), - flow_id, - r, - ); + if !event_sent_from_us(&event, r.from_device()) { + let request = VerificationRequest::from_request( + self.verifications.clone(), + self.account.clone(), + self.private_identity.lock().await.clone(), + self.store.clone(), + event.sender(), + flow_id, + r, + ); - self.requests.insert(request.flow_id().as_str().to_owned(), request); + self.requests + .insert(request.flow_id().as_str().to_owned(), request); + } else { + trace!( + sender = event.sender().as_str(), + from_device = r.from_device().as_str(), + "The received verification request was sent by us, ignoring it", + ); + } } else { trace!( sender = event.sender().as_str(), From df1fe0ebc417e57326bc23854e08cb91df9eb6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:16:40 +0200 Subject: [PATCH 12/88] crypto: Don't return a result when receiving a ready event Ready events might be invalid but we might receive a valid one later on, e.g. someone is trying to disrupt our verification, so just ignore invalid ready events. --- matrix_sdk_crypto/src/verification/machine.rs | 3 +-- matrix_sdk_crypto/src/verification/requests.rs | 11 ++++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 8278fc52..b8b21200 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -272,8 +272,7 @@ impl VerificationMachine { AnyVerificationContent::Ready(c) => { if let Some(request) = self.requests.get(flow_id.as_str()) { if request.flow_id() == &flow_id { - // TODO remove this unwrap. - request.receive_ready(event.sender(), c).unwrap(); + request.receive_ready(event.sender(), c); } else { flow_id_mismatch(); } diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 363e4f29..bb5bbfa7 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -301,15 +301,12 @@ impl VerificationRequest { }) } - #[allow(clippy::unnecessary_wraps)] - pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) -> Result<(), ()> { + pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) { let mut inner = self.inner.lock().unwrap(); if let InnerRequest::Created(s) = &*inner { *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); } - - Ok(()) } pub(crate) async fn receive_start( @@ -932,7 +929,7 @@ mod test { let content: OutgoingContent = alice_request.accept().unwrap().into(); let content = ReadyContent::try_from(&content).unwrap(); - bob_request.receive_ready(&alice_id(), &content).unwrap(); + bob_request.receive_ready(&alice_id(), &content); assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); @@ -985,7 +982,7 @@ mod test { let content: OutgoingContent = alice_request.accept().unwrap().into(); let content = ReadyContent::try_from(&content).unwrap(); - bob_request.receive_ready(&alice_id(), &content).unwrap(); + bob_request.receive_ready(&alice_id(), &content); assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); @@ -1043,7 +1040,7 @@ mod test { let content: OutgoingContent = alice_request.accept().unwrap().into(); let content = ReadyContent::try_from(&content).unwrap(); - bob_request.receive_ready(&alice_id(), &content).unwrap(); + bob_request.receive_ready(&alice_id(), &content); assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); From be53913a160ee8e7bb4e8d8a09c3d1c4da66c79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:28:10 +0200 Subject: [PATCH 13/88] crypto: Remove the redundant flow id copy --- .../src/verification/requests.rs | 41 +++++-------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index bb5bbfa7..f6d6ea64 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -230,7 +230,7 @@ impl VerificationRequest { r.private_cross_signing_identity.clone(), r.other_user_id.clone(), r.state.other_device_id.clone(), - r.state.flow_id.clone(), + r.flow_id.as_ref().to_owned(), data, ) .await?, @@ -508,7 +508,7 @@ impl RequestState { account, other_user_id: other_user_id.to_owned(), private_cross_signing_identity: private_identity, - state: Created { methods: SUPPORTED_METHODS.to_vec(), flow_id: flow_id.to_owned() }, + state: Created { methods: SUPPORTED_METHODS.to_vec() }, verification_cache: cache, store, flow_id: flow_id.to_owned().into(), @@ -527,7 +527,6 @@ impl RequestState { state: Ready { methods: content.methods().to_owned(), other_device_id: content.from_device().into(), - flow_id: self.state.flow_id, }, } } @@ -537,10 +536,6 @@ impl RequestState { 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)] @@ -548,10 +543,6 @@ struct Requested { /// The verification methods supported by the sender. pub methods: Vec, - /// The event id of the `m.key.verification.request` event which acts as an - /// unique id identifying this verification flow. - pub flow_id: FlowId, - /// The device id of the device that responded to the verification request. pub other_device_id: DeviceIdBox, } @@ -576,7 +567,6 @@ impl RequestState { other_user_id: sender.clone(), state: Requested { methods: content.methods().to_owned(), - flow_id: flow_id.clone(), other_device_id: content.from_device().into(), }, } @@ -588,30 +578,29 @@ impl RequestState { store: self.store, verification_cache: self.verification_cache, private_cross_signing_identity: self.private_cross_signing_identity, - flow_id: self.flow_id, + flow_id: self.flow_id.clone(), other_user_id: self.other_user_id, state: Ready { methods: SUPPORTED_METHODS.to_vec(), other_device_id: self.state.other_device_id.clone(), - flow_id: self.state.flow_id.clone(), }, }; - let content = match self.state.flow_id { + let content = match self.flow_id.as_ref() { FlowId::ToDevice(i) => { AnyToDeviceEventContent::KeyVerificationReady(ReadyToDeviceEventContent::new( self.account.device_id().to_owned(), SUPPORTED_METHODS.to_vec(), - i, + i.to_owned(), )) .into() } FlowId::InRoom(r, e) => ( - r, + r.to_owned(), AnyMessageEventContent::KeyVerificationReady(ReadyEventContent::new( self.account.device_id().to_owned(), SUPPORTED_METHODS.to_vec(), - Relation::new(e), + Relation::new(e.to_owned()), )), ) .into(), @@ -628,10 +617,6 @@ struct Ready { /// The device id of the device that responded to the verification request. pub other_device_id: DeviceIdBox, - - /// The event id of the `m.key.verification.request` event which acts as an - /// unique id identifying this verification flow. - pub flow_id: FlowId, } impl RequestState { @@ -813,7 +798,7 @@ impl RequestState { other_device: ReadOnlyDevice, other_identity: Option, ) -> (Sas, OutgoingContent) { - match self.state.flow_id { + match self.flow_id.as_ref() { FlowId::ToDevice(t) => { let (sas, content) = Sas::start( account, @@ -821,14 +806,14 @@ impl RequestState { other_device, store, other_identity, - Some(t), + Some(t.to_owned()), ); (sas, content) } FlowId::InRoom(r, e) => { let (sas, content) = Sas::start_in_room( - e, - r, + e.to_owned(), + r.to_owned(), account, private_identity, other_device, @@ -845,10 +830,6 @@ impl RequestState { struct Passive { /// The device id of the device that responded to the verification request. pub other_device_id: DeviceIdBox, - - /// The event id of the `m.key.verification.request` event which acts as an - /// unique id identifying this verification flow. - pub flow_id: FlowId, } #[derive(Clone, Debug)] From 80fac4bfa41b09fb4996d3c4b514f8b127fad5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:32:30 +0200 Subject: [PATCH 14/88] cyrpto: Go into passive mode if someone else replies to a request --- matrix_sdk_crypto/src/verification/requests.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index f6d6ea64..ea704f67 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -305,7 +305,11 @@ impl VerificationRequest { let mut inner = self.inner.lock().unwrap(); if let InnerRequest::Created(s) = &*inner { - *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); + if sender == self.own_user_id() && content.from_device() == self.account.device_id() { + *inner = InnerRequest::Passive(s.clone().into_passive(content)) + } else { + *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); + } } } @@ -515,6 +519,18 @@ impl RequestState { } } + fn into_passive(self, content: &ReadyContent) -> RequestState { + RequestState { + account: self.account, + flow_id: self.flow_id, + verification_cache: self.verification_cache, + private_cross_signing_identity: self.private_cross_signing_identity, + store: self.store, + other_user_id: self.other_user_id, + state: Passive { other_device_id: content.from_device().to_owned() }, + } + } + fn into_ready(self, _sender: &UserId, content: &ReadyContent) -> RequestState { // TODO check the flow id, and that the methods match what we suggested. RequestState { From 29bba0b2cac2b900256f96f89af40791200fc92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:44:51 +0200 Subject: [PATCH 15/88] crypto: Allow accepting key request while specifying our supported methods --- .../src/verification/requests.rs | 60 +++++++++++++------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index ea704f67..6d3571ea 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -266,11 +266,19 @@ impl VerificationRequest { } } - /// Accept the verification request. - pub fn accept(&self) -> Option { + /// Accept the verification request signaling that our client supports the + /// given verification methods. + /// + /// # Arguments + /// + /// * `methods` - The methods that we should advertise as supported by us. + pub fn accept_with_methods( + &self, + methods: Vec, + ) -> Option { let mut inner = self.inner.lock().unwrap(); - inner.accept().map(|c| match c { + inner.accept(methods).map(|c| match c { OutgoingContent::ToDevice(content) => { ToDeviceRequest::new(&self.other_user(), inner.other_device_id(), content).into() } @@ -280,6 +288,19 @@ impl VerificationRequest { }) } + /// Accept the verification request. + /// + /// This method will accept the request and signal that it supports the + /// `m.sas.v1`, the `m.qr_code.show.v1`, and `m.reciprocate.v1` method. + /// + /// If QR code scanning should be supported or QR code showing shouldn't be + /// supported the [`accept_with_methods()`] method should be used instead. + /// + /// [`accept_with_methods()`]: #method.accept_with_methods + pub fn accept(&self) -> Option { + self.accept_with_methods(SUPPORTED_METHODS.to_vec()) + } + /// Cancel the verification request pub fn cancel(&self) -> Option { let mut inner = self.inner.lock().unwrap(); @@ -400,9 +421,9 @@ impl InnerRequest { } } - fn accept(&mut self) -> Option { + fn accept(&mut self, methods: Vec) -> Option { if let InnerRequest::Requested(s) = self { - let (state, content) = s.clone().accept(); + let (state, content) = s.clone().accept(methods); *self = InnerRequest::Ready(state); Some(content) @@ -512,7 +533,7 @@ impl RequestState { account, other_user_id: other_user_id.to_owned(), private_cross_signing_identity: private_identity, - state: Created { methods: SUPPORTED_METHODS.to_vec() }, + state: Created { our_methods: SUPPORTED_METHODS.to_vec() }, verification_cache: cache, store, flow_id: flow_id.to_owned().into(), @@ -541,7 +562,8 @@ impl RequestState { store: self.store, other_user_id: self.other_user_id, state: Ready { - methods: content.methods().to_owned(), + their_methods: content.methods().to_owned(), + our_methods: self.state.our_methods, other_device_id: content.from_device().into(), }, } @@ -550,14 +572,14 @@ impl RequestState { #[derive(Clone, Debug)] struct Created { - /// The verification methods supported by the sender. - pub methods: Vec, + /// The verification methods supported by us. + pub our_methods: Vec, } #[derive(Clone, Debug)] struct Requested { /// The verification methods supported by the sender. - pub methods: Vec, + pub their_methods: Vec, /// The device id of the device that responded to the verification request. pub other_device_id: DeviceIdBox, @@ -582,13 +604,13 @@ impl RequestState { flow_id: flow_id.to_owned().into(), other_user_id: sender.clone(), state: Requested { - methods: content.methods().to_owned(), + their_methods: content.methods().to_owned(), other_device_id: content.from_device().into(), }, } } - fn accept(self) -> (RequestState, OutgoingContent) { + fn accept(self, methods: Vec) -> (RequestState, OutgoingContent) { let state = RequestState { account: self.account.clone(), store: self.store, @@ -597,7 +619,8 @@ impl RequestState { flow_id: self.flow_id.clone(), other_user_id: self.other_user_id, state: Ready { - methods: SUPPORTED_METHODS.to_vec(), + their_methods: self.state.their_methods, + our_methods: methods.clone(), other_device_id: self.state.other_device_id.clone(), }, }; @@ -606,7 +629,7 @@ impl RequestState { FlowId::ToDevice(i) => { AnyToDeviceEventContent::KeyVerificationReady(ReadyToDeviceEventContent::new( self.account.device_id().to_owned(), - SUPPORTED_METHODS.to_vec(), + methods, i.to_owned(), )) .into() @@ -615,7 +638,7 @@ impl RequestState { r.to_owned(), AnyMessageEventContent::KeyVerificationReady(ReadyEventContent::new( self.account.device_id().to_owned(), - SUPPORTED_METHODS.to_vec(), + methods, Relation::new(e.to_owned()), )), ) @@ -628,8 +651,11 @@ impl RequestState { #[derive(Clone, Debug)] struct Ready { - /// The verification methods supported by the sender. - pub methods: Vec, + /// The verification methods supported by the other side. + pub their_methods: Vec, + + /// 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: DeviceIdBox, From c547f384bc948f5f170dd7e5fa43a62fbb91101f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:49:18 +0200 Subject: [PATCH 16/88] crypto: Fix the method to transition from a request into a SAS verification --- .../src/verification/requests.rs | 102 +++++++++++++----- 1 file changed, 77 insertions(+), 25 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 6d3571ea..48caf4d0 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -368,21 +368,44 @@ impl VerificationRequest { } } - pub(crate) fn start( + /// Transition from this verification request into a SAS verification flow. + pub async fn start_sas( &self, - device: ReadOnlyDevice, - user_identity: Option, - ) -> Option<(Sas, OutgoingContent)> { - match &*self.inner.lock().unwrap() { - InnerRequest::Ready(s) => Some(s.clone().start_sas( - s.store.clone(), - s.account.clone(), - s.private_cross_signing_identity.clone(), - device, - user_identity, - )), + ) -> Result, CryptoStoreError> { + let inner = self.inner.lock().unwrap().clone(); + + Ok(match &inner { + InnerRequest::Ready(s) => { + if let Some((sas, content)) = s + .clone() + .start_sas( + s.store.clone(), + s.account.clone(), + s.private_cross_signing_identity.clone(), + ) + .await? + { + self.verification_cache.insert_sas(sas.clone()); + + let request = match content { + OutgoingContent::ToDevice(content) => ToDeviceRequest::new( + &self.other_user(), + inner.other_device_id(), + content, + ) + .into(), + OutgoingContent::Room(room_id, content) => { + RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into() + } + }; + + Some((sas, request)) + } else { + None + } + } _ => None, - } + }) } } @@ -832,20 +855,39 @@ impl RequestState { Ok(()) } - fn start_sas( + async fn start_sas( self, store: Arc, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, - other_device: ReadOnlyDevice, - other_identity: Option, - ) -> (Sas, OutgoingContent) { - match self.flow_id.as_ref() { + ) -> Result, CryptoStoreError> { + if !self.state.their_methods.contains(&VerificationMethod::MSasV1) { + return Ok(None); + } + + // TODO signal why starting the sas flow doesn't work? + let other_identity = store.get_user_identity(&self.other_user_id).await?; + + let device = if let Some(device) = + self.store.get_device(&self.other_user_id, &self.state.other_device_id).await? + { + device + } else { + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't start the SAS verificaiton flow, the device that \ + accepted the verification doesn't exist" + ); + return Ok(None); + }; + + Ok(Some(match self.flow_id.as_ref() { FlowId::ToDevice(t) => { let (sas, content) = Sas::start( account, private_identity, - other_device, + device, store, other_identity, Some(t.to_owned()), @@ -858,13 +900,13 @@ impl RequestState { r.to_owned(), account, private_identity, - other_device, + device, store, other_identity, ); (sas, content) } - } + })) } } @@ -978,6 +1020,10 @@ mod test { changes.devices.new.push(bob_device.clone()); alice_store.save_changes(changes).await.unwrap(); + let mut changes = Changes::default(); + changes.devices.new.push(alice_device.clone()); + bob_store.save_changes(changes).await.unwrap(); + let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id()); let bob_request = VerificationRequest::new( @@ -1010,9 +1056,10 @@ mod test { assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); - let (bob_sas, start_content) = bob_request.start(alice_device, None).unwrap(); + let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); - let content = StartContent::try_from(&start_content).unwrap(); + let content: OutgoingContent = request.into(); + 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(); let alice_sas = @@ -1039,6 +1086,10 @@ mod test { changes.devices.new.push(bob_device.clone()); alice_store.save_changes(changes).await.unwrap(); + let mut changes = Changes::default(); + changes.devices.new.push(alice_device.clone()); + bob_store.save_changes(changes).await.unwrap(); + let bob_request = VerificationRequest::new_to_device( VerificationCache::new(), bob, @@ -1068,9 +1119,10 @@ mod test { assert!(bob_request.is_ready()); assert!(alice_request.is_ready()); - let (bob_sas, start_content) = bob_request.start(alice_device, None).unwrap(); + let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); - let content = StartContent::try_from(&start_content).unwrap(); + let content: OutgoingContent = request.into(); + 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(); let alice_sas = From b7986a5153b2450c4e292400d1fd64fbbcd2b8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:50:35 +0200 Subject: [PATCH 17/88] crypto: Add a couple more accessors for the verification request --- .../src/verification/requests.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 48caf4d0..71ec2463 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -177,11 +177,32 @@ impl VerificationRequest { &self.other_user_id } + /// Has the verification request been answered by another device. + pub fn is_passive(&self) -> bool { + matches!(&*self.inner.lock().unwrap(), InnerRequest::Passive(_)) + } + /// Is the verification request ready to start a verification flow. pub fn is_ready(&self) -> bool { matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_)) } + /// Get the supported verification methods of the other side. + /// + /// Will be present only if the other side requested the verification or if + /// we're in the ready state. + pub fn their_supported_methods(&self) -> Vec { + match &*self.inner.lock().unwrap() { + InnerRequest::Requested(r) => Some(r.state.their_methods.clone()), + InnerRequest::Ready(r) => Some(r.state.their_methods.clone()), + InnerRequest::Created(_) + | InnerRequest::Passive(_) + | InnerRequest::Done(_) + | InnerRequest::Cancelled(_) => None, + } + .unwrap_or_default() + } + /// Get the unique ID of this verification request pub fn flow_id(&self) -> &FlowId { &self.flow_id From 58d3b42a60addcd5f656a2d020d9731e166a5d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 14 Jun 2021 17:51:13 +0200 Subject: [PATCH 18/88] crypto: Don't allow QR code generation if we or the other can't handle it --- matrix_sdk_crypto/src/verification/requests.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 71ec2463..6ba52aa3 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -726,6 +726,15 @@ impl RequestState { async fn generate_qr_code(&self) -> Result, CryptoStoreError> { // TODO return an error explaining why we can't generate a QR code? + + // If we didn't state that we support showing QR codes or if the other + // side doesn't support scanning QR codes bail early. + if !self.state.our_methods.contains(&VerificationMethod::MQrCodeShowV1) + || !self.state.their_methods.contains(&VerificationMethod::MQrScanShowV1) + { + return Ok(None); + } + let device = if let Some(device) = self.store.get_device(&self.other_user_id, &self.state.other_device_id).await? { From 5d38bc3802c420a87fbca8e8f3b4479fd90f9ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 15 Jun 2021 21:14:12 +0200 Subject: [PATCH 19/88] crypto: Scope the verification requests behind the other user id --- matrix_sdk/examples/emoji_verification.rs | 2 +- matrix_sdk/src/client.rs | 6 ++-- matrix_sdk_crypto/src/machine.rs | 3 +- matrix_sdk_crypto/src/verification/machine.rs | 33 +++++++++++++------ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 967154f4..4ff70983 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -129,7 +129,7 @@ async fn login( if let MessageType::VerificationRequest(_) = &m.content.msgtype { let request = client - .get_verification_request(&m.event_id) + .get_verification_request(&m.sender, &m.event_id) .await .expect("Request object wasn't created"); diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index a5efaf9d..2f449684 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -2192,16 +2192,18 @@ impl Client { .map(|sas| Sas { inner: sas, client: self.clone() }) } - /// Get a `VerificationRequest` object with the given flow id. + /// Get a `VerificationRequest` object for the given user with the given + /// flow id. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub async fn get_verification_request( &self, + user_id: &UserId, flow_id: impl AsRef, ) -> Option { let olm = self.base_client.olm_machine().await?; - olm.get_verification_request(flow_id) + olm.get_verification_request(user_id, flow_id) .map(|r| VerificationRequest { inner: r, client: self.clone() }) } diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 258c61b1..5b1cfa00 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -725,9 +725,10 @@ impl OlmMachine { /// Get a verification request object with the given flow id. pub fn get_verification_request( &self, + user_id: &UserId, flow_id: impl AsRef, ) -> Option { - self.verification_machine.get_request(flow_id) + self.verification_machine.get_request(user_id, flow_id) } async fn update_one_time_key_count(&self, key_count: &BTreeMap) { diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index b8b21200..5478a16e 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -40,7 +40,7 @@ pub struct VerificationMachine { private_identity: Arc>, pub(crate) store: Arc, verifications: VerificationCache, - requests: Arc>, + requests: Arc>>, } impl VerificationMachine { @@ -91,8 +91,19 @@ impl VerificationMachine { Ok((sas, request)) } - pub fn get_request(&self, flow_id: impl AsRef) -> Option { - self.requests.get(flow_id.as_ref()).map(|s| s.clone()) + pub fn get_request( + &self, + user_id: &UserId, + flow_id: impl AsRef, + ) -> Option { + self.requests.get(user_id).and_then(|v| v.get(flow_id.as_ref()).map(|s| s.clone())) + } + + fn insert_request(&self, request: VerificationRequest) { + self.requests + .entry(request.other_user().to_owned()) + .or_insert_with(DashMap::new) + .insert(request.flow_id().as_str().to_owned(), request); } pub fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { @@ -145,7 +156,10 @@ impl VerificationMachine { } pub fn garbage_collect(&self) { - self.requests.retain(|_, r| !(r.is_done() || r.is_cancelled())); + for user_verification in self.requests.iter() { + user_verification.retain(|_, v| !(v.is_done() || v.is_cancelled())); + } + self.requests.retain(|_, v| !v.is_empty()); for request in self.verifications.garbage_collect() { self.verifications.add_request(request) @@ -234,8 +248,7 @@ impl VerificationMachine { r, ); - self.requests - .insert(request.flow_id().as_str().to_owned(), request); + self.insert_request(request); } else { trace!( sender = event.sender().as_str(), @@ -260,7 +273,7 @@ impl VerificationMachine { } } AnyVerificationContent::Cancel(c) => { - if let Some(verification) = self.get_request(flow_id.as_str()) { + if let Some(verification) = self.get_request(event.sender(), flow_id.as_str()) { verification.receive_cancel(event.sender(), c); } @@ -270,7 +283,7 @@ impl VerificationMachine { } } AnyVerificationContent::Ready(c) => { - if let Some(request) = self.requests.get(flow_id.as_str()) { + if let Some(request) = self.get_request(event.sender(), flow_id.as_str()) { if request.flow_id() == &flow_id { request.receive_ready(event.sender(), c); } else { @@ -279,7 +292,7 @@ impl VerificationMachine { } } AnyVerificationContent::Start(c) => { - if let Some(request) = self.requests.get(flow_id.as_str()) { + if let Some(request) = self.get_request(event.sender(), flow_id.as_str()) { if request.flow_id() == &flow_id { request.receive_start(event.sender(), c).await? } else { @@ -345,7 +358,7 @@ impl VerificationMachine { } } AnyVerificationContent::Done(c) => { - if let Some(verification) = self.get_request(flow_id.as_str()) { + if let Some(verification) = self.get_request(event.sender(), flow_id.as_str()) { verification.receive_done(event.sender(), c); } From f8b09d4537f79fb457c6e424a1e99b09cbc866ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 15 Jun 2021 21:15:11 +0200 Subject: [PATCH 20/88] crypto: Remember who started the verification request --- matrix_sdk_crypto/src/verification/requests.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 6ba52aa3..12459b04 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -70,6 +70,7 @@ pub struct VerificationRequest { flow_id: Arc, other_user_id: Arc, inner: Arc>, + we_started: bool, } impl VerificationRequest { @@ -100,6 +101,7 @@ impl VerificationRequest { flow_id: flow_id.into(), inner, other_user_id: other_user.to_owned().into(), + we_started: true, } } @@ -128,6 +130,7 @@ impl VerificationRequest { flow_id: flow_id.into(), inner, other_user_id: other_user.to_owned().into(), + we_started: true, } } @@ -213,6 +216,11 @@ impl VerificationRequest { self.account.user_id() == self.other_user() } + /// Did we initiate the verification request + pub fn we_started(&self) -> bool { + self.we_started + } + /// Has the verification flow that was started with this request finished. pub fn is_done(&self) -> bool { matches!(&*self.inner.lock().unwrap(), InnerRequest::Done(_)) @@ -284,6 +292,7 @@ impl VerificationRequest { account, other_user_id: sender.to_owned().into(), flow_id: flow_id.into(), + we_started: false, } } From d212e7df18497c653e2c1a326e6437a674dd47a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 15 Jun 2021 21:16:01 +0200 Subject: [PATCH 21/88] crypto: Add some more accessors to the verification requests --- matrix_sdk_crypto/src/verification/requests.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 12459b04..859106eb 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -180,6 +180,22 @@ impl VerificationRequest { &self.other_user_id } + /// Get the room id if the verification is happening inside a room. + pub fn room_id(&self) -> Option<&RoomId> { + match self.flow_id.as_ref() { + FlowId::ToDevice(_) => None, + FlowId::InRoom(r, _) => Some(r), + } + } + + /// Get the `CancelCode` that cancelled this verification request. + pub fn cancel_code(&self) -> Option { + match &*self.inner.lock().unwrap() { + InnerRequest::Cancelled(c) => Some(c.state.cancel_code.to_owned()), + _ => None, + } + } + /// Has the verification request been answered by another device. pub fn is_passive(&self) -> bool { matches!(&*self.inner.lock().unwrap(), InnerRequest::Passive(_)) From 34703bc0d66c02919b8d509e8064a29e410da128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Jun 2021 10:03:47 +0200 Subject: [PATCH 22/88] crypto: Add a method to get all verification requests of a certain user --- matrix_sdk_crypto/src/machine.rs | 5 +++++ matrix_sdk_crypto/src/verification/machine.rs | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 5b1cfa00..a22e7929 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -731,6 +731,11 @@ impl OlmMachine { self.verification_machine.get_request(user_id, flow_id) } + /// Get all the verification requests of a given user. + pub fn get_verification_requests(&self, user_id: &UserId) -> Vec { + self.verification_machine.get_requests(user_id) + } + async fn update_one_time_key_count(&self, key_count: &BTreeMap) { self.account.update_uploaded_key_count(key_count).await; } diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 5478a16e..9a1162fe 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -99,6 +99,13 @@ impl VerificationMachine { self.requests.get(user_id).and_then(|v| v.get(flow_id.as_ref()).map(|s| s.clone())) } + pub fn get_requests(&self, user_id: &UserId) -> Vec { + self.requests + .get(user_id) + .map(|v| v.iter().map(|i| i.value().clone()).collect()) + .unwrap_or_default() + } + fn insert_request(&self, request: VerificationRequest) { self.requests .entry(request.other_user().to_owned()) From baee5b2d1179eb6e850b31e5888d4e4ee9d35807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Jun 2021 10:06:53 +0200 Subject: [PATCH 23/88] crytpo: Couple more accessors for the verification request --- .../src/verification/requests.rs | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 859106eb..9711caa6 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -180,6 +180,18 @@ impl VerificationRequest { &self.other_user_id } + /// The id of the other device that is participating in this verification. + pub fn other_device_id(&self) -> Option { + match &*self.inner.lock().unwrap() { + InnerRequest::Requested(r) => Some(r.state.other_device_id.clone()), + InnerRequest::Ready(r) => Some(r.state.other_device_id.clone()), + InnerRequest::Created(_) + | InnerRequest::Passive(_) + | InnerRequest::Done(_) + | InnerRequest::Cancelled(_) => None, + } + } + /// Get the room id if the verification is happening inside a room. pub fn room_id(&self) -> Option<&RoomId> { match self.flow_id.as_ref() { @@ -210,7 +222,7 @@ impl VerificationRequest { /// /// Will be present only if the other side requested the verification or if /// we're in the ready state. - pub fn their_supported_methods(&self) -> Vec { + pub fn their_supported_methods(&self) -> Option> { match &*self.inner.lock().unwrap() { InnerRequest::Requested(r) => Some(r.state.their_methods.clone()), InnerRequest::Ready(r) => Some(r.state.their_methods.clone()), @@ -219,7 +231,21 @@ impl VerificationRequest { | InnerRequest::Done(_) | InnerRequest::Cancelled(_) => None, } - .unwrap_or_default() + } + + /// Get our own supported verification methods that we advertised. + /// + /// Will be present only we requested the verification or if we're in the + /// ready state. + pub fn our_supported_methods(&self) -> Option> { + match &*self.inner.lock().unwrap() { + InnerRequest::Created(r) => Some(r.state.our_methods.clone()), + InnerRequest::Ready(r) => Some(r.state.our_methods.clone()), + InnerRequest::Requested(_) + | InnerRequest::Passive(_) + | InnerRequest::Done(_) + | InnerRequest::Cancelled(_) => None, + } } /// Get the unique ID of this verification request From 3cf843d24f8e41e59697bb9225d24aa9f3197c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Jun 2021 12:17:11 +0200 Subject: [PATCH 24/88] matrix-sdk: Rework the public API for answering verifications --- matrix_sdk/examples/emoji_verification.rs | 75 ++++++----- matrix_sdk/src/client.rs | 36 ++++-- matrix_sdk/src/device.rs | 6 +- matrix_sdk/src/lib.rs | 11 +- matrix_sdk/src/verification/mod.rs | 120 ++++++++++++++++++ matrix_sdk/src/verification/qrcode.rs | 87 +++++++++++++ matrix_sdk/src/verification/requests.rs | 132 ++++++++++++++++++++ matrix_sdk/src/{ => verification}/sas.rs | 49 +++----- matrix_sdk/src/verification_request.rs | 49 -------- matrix_sdk_base/src/client.rs | 22 +--- matrix_sdk_base/src/store/sled_store/mod.rs | 1 + 11 files changed, 426 insertions(+), 162 deletions(-) create mode 100644 matrix_sdk/src/verification/mod.rs create mode 100644 matrix_sdk/src/verification/qrcode.rs create mode 100644 matrix_sdk/src/verification/requests.rs rename matrix_sdk/src/{ => verification}/sas.rs (77%) delete mode 100644 matrix_sdk/src/verification_request.rs diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 4ff70983..ac212aec 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -11,11 +11,12 @@ use matrix_sdk::{ self, events::{room::message::MessageType, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent}, identifiers::UserId, - Client, LoopCtrl, Sas, SyncSettings, + verification::{SasVerification, Verification}, + Client, LoopCtrl, SyncSettings, }; use url::Url; -async fn wait_for_confirmation(client: Client, sas: Sas) { +async fn wait_for_confirmation(client: Client, sas: SasVerification) { println!("Does the emoji match: {:?}", sas.emoji()); let mut input = String::new(); @@ -34,7 +35,7 @@ async fn wait_for_confirmation(client: Client, sas: Sas) { } } -fn print_result(sas: &Sas) { +fn print_result(sas: &SasVerification) { let device = sas.other_device(); println!( @@ -80,37 +81,35 @@ async fn login( for event in response.to_device.events.iter().filter_map(|e| e.deserialize().ok()) { match event { AnyToDeviceEvent::KeyVerificationStart(e) => { - let sas = client - .get_verification(&e.sender, &e.content.transaction_id) - .await - .expect("Sas object wasn't created"); - println!( - "Starting verification with {} {}", - &sas.other_device().user_id(), - &sas.other_device().device_id() - ); - print_devices(&e.sender, client).await; - sas.accept().await.unwrap(); + if let Some(Verification::SasV1(sas)) = + client.get_verification(&e.sender, &e.content.transaction_id).await + { + println!( + "Starting verification with {} {}", + &sas.other_device().user_id(), + &sas.other_device().device_id() + ); + print_devices(&e.sender, client).await; + sas.accept().await.unwrap(); + } } AnyToDeviceEvent::KeyVerificationKey(e) => { - let sas = client - .get_verification(&e.sender, &e.content.transaction_id) - .await - .expect("Sas object wasn't created"); - - tokio::spawn(wait_for_confirmation((*client).clone(), sas)); + if let Some(Verification::SasV1(sas)) = + client.get_verification(&e.sender, &e.content.transaction_id).await + { + tokio::spawn(wait_for_confirmation((*client).clone(), sas)); + } } AnyToDeviceEvent::KeyVerificationMac(e) => { - let sas = client - .get_verification(&e.sender, &e.content.transaction_id) - .await - .expect("Sas object wasn't created"); - - if sas.is_done() { - print_result(&sas); - print_devices(&e.sender, client).await; + if let Some(Verification::SasV1(sas)) = + client.get_verification(&e.sender, &e.content.transaction_id).await + { + if sas.is_done() { + print_result(&sas); + print_devices(&e.sender, client).await; + } } } @@ -140,28 +139,28 @@ async fn login( } } AnySyncMessageEvent::KeyVerificationKey(e) => { - let sas = client + if let Some(Verification::SasV1(sas)) = client .get_verification( &e.sender, e.content.relation.event_id.as_str(), ) .await - .expect("Sas object wasn't created"); - - tokio::spawn(wait_for_confirmation((*client).clone(), sas)); + { + tokio::spawn(wait_for_confirmation((*client).clone(), sas)); + } } AnySyncMessageEvent::KeyVerificationMac(e) => { - let sas = client + if let Some(Verification::SasV1(sas)) = client .get_verification( &e.sender, e.content.relation.event_id.as_str(), ) .await - .expect("Sas object wasn't created"); - - if sas.is_done() { - print_result(&sas); - print_devices(&e.sender, client).await; + { + if sas.is_done() { + print_result(&sas); + print_devices(&e.sender, client).await; + } } } _ => (), diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 2f449684..62f04092 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -126,8 +126,7 @@ use ruma::{ #[cfg(feature = "encryption")] use crate::{ device::{Device, UserDevices}, - sas::Sas, - verification_request::VerificationRequest, + verification::{QrVerification, SasVerification, Verification, VerificationRequest}, }; use crate::{ error::HttpError, @@ -2182,14 +2181,19 @@ impl Client { Ok(response) } - /// Get a `Sas` verification object with the given flow id. + /// Get a verification object with the given flow id. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] - pub async fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { - self.base_client - .get_verification(user_id, flow_id) - .await - .map(|sas| Sas { inner: sas, client: self.clone() }) + pub async fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { + let olm = self.base_client.olm_machine().await?; + olm.get_verification(user_id, flow_id).map(|v| match v { + matrix_sdk_base::crypto::Verification::SasV1(s) => { + SasVerification { inner: s, client: self.clone() }.into() + } + matrix_sdk_base::crypto::Verification::QrV1(qr) => { + QrVerification { inner: qr, client: self.clone() }.into() + } + }) } /// Get a `VerificationRequest` object for the given user with the given @@ -2697,6 +2701,22 @@ impl Client { let request = whoami::Request::new(); self.send(request, None).await } + + pub(crate) async fn send_verification_request( + &self, + request: matrix_sdk_base::crypto::OutgoingVerificationRequest, + ) -> Result<()> { + match request { + matrix_sdk_base::crypto::OutgoingVerificationRequest::ToDevice(t) => { + self.send_to_device(&t).await?; + } + matrix_sdk_base::crypto::OutgoingVerificationRequest::InRoom(r) => { + self.room_send_helper(&r).await?; + } + } + + Ok(()) + } } #[cfg(test)] diff --git a/matrix_sdk/src/device.rs b/matrix_sdk/src/device.rs index 37bf06dc..275060a9 100644 --- a/matrix_sdk/src/device.rs +++ b/matrix_sdk/src/device.rs @@ -20,7 +20,7 @@ use matrix_sdk_base::crypto::{ }; use ruma::{DeviceId, DeviceIdBox}; -use crate::{error::Result, Client, Sas}; +use crate::{error::Result, verification::SasVerification, Client}; #[derive(Clone, Debug)] /// A device represents a E2EE capable client of an user. @@ -62,11 +62,11 @@ impl Device { /// let verification = device.start_verification().await.unwrap(); /// # }); /// ``` - pub async fn start_verification(&self) -> Result { + pub async fn start_verification(&self) -> Result { let (sas, request) = self.inner.start_verification().await?; self.client.send_to_device(&request).await?; - Ok(Sas { inner: sas, client: self.client.clone() }) + Ok(SasVerification { inner: sas, client: self.client.clone() }) } /// Is the device trusted. diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 4473662e..eb28e8bd 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -115,9 +115,7 @@ mod room_member; #[cfg(feature = "encryption")] mod device; #[cfg(feature = "encryption")] -mod sas; -#[cfg(feature = "encryption")] -mod verification_request; +pub mod verification; pub use client::{Client, ClientConfig, LoopCtrl, RequestConfig, SyncSettings}; #[cfg(feature = "encryption")] @@ -127,12 +125,5 @@ pub use error::{Error, HttpError, Result}; pub use event_handler::{CustomEvent, EventHandler}; pub use http_client::HttpSend; 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/mod.rs b/matrix_sdk/src/verification/mod.rs new file mode 100644 index 00000000..a742badf --- /dev/null +++ b/matrix_sdk/src/verification/mod.rs @@ -0,0 +1,120 @@ +// Copyright 2021 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Interactive verification for E2EE capable users and devices in Matrix. +//! +//! The SDK supports interactive verification of devices and users, this module +//! contains types that model and support different verification flows. +//! +//! A verification flow usually starts its life as a [VerificationRequest], the +//! request can then be accepted, or it needs to be accepted by the other side +//! of the verification flow. +//! +//! Once both sides have agreed to pereform the verification, and the +//! [VerificationRequest::is_ready()] method returns true, the verification can +//! transition into one of the supported verification flows: +//! +//! * [SasVerification] - Interactive verification using a short authentication +//! string. +//! * [QrVerification] - Interactive verification using QR codes. + +mod qrcode; +mod requests; +mod sas; + +pub use qrcode::QrVerification; +pub use requests::VerificationRequest; +pub use sas::SasVerification; + +/// An enum over the different verification types the SDK supports. +#[derive(Debug, Clone)] +pub enum Verification { + /// The `m.sas.v1` verification variant. + SasV1(SasVerification), + /// The `m.qr_code.*.v1` verification variant. + QrV1(QrVerification), +} + +impl Verification { + /// Try to deconstruct this verification enum into a SAS verification. + pub fn sas(self) -> Option { + if let Verification::SasV1(sas) = self { + Some(sas) + } else { + None + } + } + + /// Try to deconstruct this verification enum into a QR code verification. + pub fn qr(self) -> Option { + if let Verification::QrV1(qr) = self { + Some(qr) + } else { + None + } + } + + /// Has this verification finished. + pub fn is_done(&self) -> bool { + match self { + Verification::SasV1(s) => s.is_done(), + Verification::QrV1(qr) => qr.is_done(), + } + } + + /// Has the verification been cancelled. + pub fn is_cancelled(&self) -> bool { + match self { + Verification::SasV1(s) => s.is_cancelled(), + Verification::QrV1(qr) => qr.is_cancelled(), + } + } + + /// Get our own user id. + pub fn own_user_id(&self) -> &ruma::UserId { + match self { + Verification::SasV1(v) => v.own_user_id(), + Verification::QrV1(v) => v.own_user_id(), + } + } + + /// Get the user id of the other user participating in this verification + /// flow. + pub fn other_user_id(&self) -> &ruma::UserId { + match self { + Verification::SasV1(v) => v.inner.other_user_id(), + Verification::QrV1(v) => v.inner.other_user_id(), + } + } + + /// Is this a verification that is veryfying one of our own devices. + pub fn is_self_verification(&self) -> bool { + match self { + Verification::SasV1(v) => v.is_self_verification(), + Verification::QrV1(v) => v.is_self_verification(), + } + } +} + +impl From for Verification { + fn from(sas: SasVerification) -> Self { + Self::SasV1(sas) + } +} + +impl From for Verification { + fn from(qr: QrVerification) -> Self { + Self::QrV1(qr) + } +} diff --git a/matrix_sdk/src/verification/qrcode.rs b/matrix_sdk/src/verification/qrcode.rs new file mode 100644 index 00000000..f28d11b5 --- /dev/null +++ b/matrix_sdk/src/verification/qrcode.rs @@ -0,0 +1,87 @@ +// Copyright 2021 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use matrix_sdk_base::crypto::{ + matrix_qrcode::{qrcode::QrCode, EncodingError}, + QrVerification as BaseQrVerification, +}; +use ruma::UserId; + +use crate::{Client, Result}; + +/// An object controlling QR code style key verification flows. +#[derive(Debug, Clone)] +pub struct QrVerification { + pub(crate) inner: BaseQrVerification, + pub(crate) client: Client, +} + +impl QrVerification { + /// Get our own user id. + pub fn own_user_id(&self) -> &UserId { + self.inner.user_id() + } + + /// Is this a verification that is veryfying one of our own devices. + pub fn is_self_verification(&self) -> bool { + self.inner.is_self_verification() + } + + /// Has this verification finished. + pub fn is_done(&self) -> bool { + self.inner.is_done() + } + + /// Has the verification been cancelled. + pub fn is_cancelled(&self) -> bool { + self.inner.is_cancelled() + } + + /// Generate a QR code object that is representing this verification flow. + /// + /// The `QrCode` can then be rendered as an image or as an unicode string. + /// + /// The [`to_bytes()`](#method.to_bytes) method can be used to instead + /// output the raw bytes that should be encoded as a QR code. + pub fn to_qr_code(&self) -> std::result::Result { + self.inner.to_qr_code() + } + + /// Generate a the raw bytes that should be encoded as a QR code is + /// representing this verification flow. + /// + /// The [`to_qr_code()`](#method.to_qr_code) method can be used to instead + /// output a `QrCode` object that can be rendered. + pub fn to_bytes(&self) -> std::result::Result, EncodingError> { + self.inner.to_bytes() + } + + /// Confirm that the other side has scanned our QR code. + pub async fn confirm(&self) -> Result<()> { + if let Some(request) = self.inner.confirm_scanning() { + self.client.send_verification_request(request).await?; + } + + Ok(()) + } + + /// Abort the verification flow and notify the other side that we did so. + pub async fn cancel(&self) -> Result<()> { + if let Some(request) = self.inner.cancel() { + self.client.send_verification_request(request).await?; + } + + Ok(()) + } +} diff --git a/matrix_sdk/src/verification/requests.rs b/matrix_sdk/src/verification/requests.rs new file mode 100644 index 00000000..5a94c7b9 --- /dev/null +++ b/matrix_sdk/src/verification/requests.rs @@ -0,0 +1,132 @@ +// Copyright 2021 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use matrix_sdk_base::crypto::VerificationRequest as BaseVerificationRequest; +use ruma::events::key::verification::VerificationMethod; + +use super::{QrVerification, SasVerification}; +use crate::{Client, Result}; + +/// An object controlling the interactive verification flow. +#[derive(Debug, Clone)] +pub struct VerificationRequest { + pub(crate) inner: BaseVerificationRequest, + pub(crate) client: Client, +} + +impl VerificationRequest { + /// Has this verification finished. + pub fn is_done(&self) -> bool { + self.inner.is_done() + } + + /// Has the verification been cancelled. + pub fn is_cancelled(&self) -> bool { + self.inner.is_cancelled() + } + + /// Get our own user id. + pub fn own_user_id(&self) -> &ruma::UserId { + self.inner.own_user_id() + } + + /// Has the verification request been answered by another device. + pub fn is_passive(&self) -> bool { + self.inner.is_passive() + } + + /// Is the verification request ready to start a verification flow. + pub fn is_ready(&self) -> bool { + self.inner.is_ready() + } + + /// Get the user id of the other user participating in this verification + /// flow. + pub fn other_user_id(&self) -> &ruma::UserId { + self.inner.other_user() + } + + /// Is this a verification that is veryfying one of our own devices. + pub fn is_self_verification(&self) -> bool { + self.inner.is_self_verification() + } + + /// Get the supported verification methods of the other side. + /// + /// Will be present only if the other side requested the verification or if + /// we're in the ready state. + pub fn their_supported_methods(&self) -> Option> { + self.inner.their_supported_methods() + } + + /// Accept the verification request. + /// + /// This method will accept the request and signal that it supports the + /// `m.sas.v1`, the `m.qr_code.show.v1`, and `m.reciprocate.v1` method. + /// + /// If QR code scanning should be supported or QR code showing shouldn't be + /// supported the [`accept_with_methods()`] method should be used instead. + /// + /// [`accept_with_methods()`]: #method.accept_with_methods + pub async fn accept(&self) -> Result<()> { + if let Some(request) = self.inner.accept() { + self.client.send_verification_request(request).await?; + } + + Ok(()) + } + + /// Accept the verification request signaling that our client supports the + /// given verification methods. + /// + /// # Arguments + /// + /// * `methods` - The methods that we should advertise as supported by us. + pub async fn accept_with_methods(&self, methods: Vec) -> Result<()> { + if let Some(request) = self.inner.accept_with_methods(methods) { + self.client.send_verification_request(request).await?; + } + + Ok(()) + } + + /// Generate a QR code + pub async fn generate_qr_code(&self) -> Result> { + Ok(self + .inner + .generate_qr_code() + .await? + .map(|qr| QrVerification { inner: qr, client: self.client.clone() })) + } + + /// Transition from this verification request into a SAS verification flow. + pub async fn start_sas(&self) -> Result> { + if let Some((sas, request)) = self.inner.start_sas().await? { + self.client.send_verification_request(request).await?; + + Ok(Some(SasVerification { inner: sas, client: self.client.clone() })) + } else { + Ok(None) + } + } + + /// Cancel the verification request + pub async fn cancel(&self) -> Result<()> { + if let Some(request) = self.inner.cancel() { + self.client.send_verification_request(request).await?; + } + + Ok(()) + } +} diff --git a/matrix_sdk/src/sas.rs b/matrix_sdk/src/verification/sas.rs similarity index 77% rename from matrix_sdk/src/sas.rs rename to matrix_sdk/src/verification/sas.rs index 17db6a2f..124b6504 100644 --- a/matrix_sdk/src/sas.rs +++ b/matrix_sdk/src/verification/sas.rs @@ -12,21 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use matrix_sdk_base::crypto::{ - AcceptSettings, OutgoingVerificationRequest, ReadOnlyDevice, Sas as BaseSas, -}; +use matrix_sdk_base::crypto::{AcceptSettings, ReadOnlyDevice, Sas as BaseSas}; use ruma::UserId; use crate::{error::Result, Client}; /// An object controlling the interactive verification flow. #[derive(Debug, Clone)] -pub struct Sas { +pub struct SasVerification { pub(crate) inner: BaseSas, pub(crate) client: Client, } -impl Sas { +impl SasVerification { /// Accept the interactive verification flow. pub async fn accept(&self) -> Result<()> { self.accept_with_settings(Default::default()).await @@ -45,7 +43,7 @@ impl Sas { /// # use futures::executor::block_on; /// # use url::Url; /// # use ruma::identifiers::user_id; - /// use matrix_sdk::Sas; + /// use matrix_sdk::verification::SasVerification; /// use matrix_sdk_base::crypto::AcceptSettings; /// use matrix_sdk::events::key::verification::ShortAuthenticationString; /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -56,6 +54,8 @@ impl Sas { /// let sas = client /// .get_verification(&user_id, flow_id) /// .await + /// .unwrap() + /// .sas() /// .unwrap(); /// /// let only_decimal = AcceptSettings::with_allowed_methods( @@ -65,15 +65,8 @@ impl Sas { /// # }); /// ``` pub async fn accept_with_settings(&self, settings: AcceptSettings) -> Result<()> { - if let Some(req) = self.inner.accept_with_settings(settings) { - match req { - OutgoingVerificationRequest::ToDevice(r) => { - self.client.send_to_device(&r).await?; - } - OutgoingVerificationRequest::InRoom(r) => { - self.client.room_send_helper(&r).await?; - } - } + if let Some(request) = self.inner.accept_with_settings(settings) { + self.client.send_verification_request(request).await?; } Ok(()) } @@ -82,16 +75,8 @@ impl Sas { pub async fn confirm(&self) -> Result<()> { let (request, signature) = self.inner.confirm().await?; - match request { - Some(OutgoingVerificationRequest::InRoom(r)) => { - self.client.room_send_helper(&r).await?; - } - - Some(OutgoingVerificationRequest::ToDevice(r)) => { - self.client.send_to_device(&r).await?; - } - - None => (), + if let Some(request) = request { + self.client.send_verification_request(request).await?; } if let Some(s) = signature { @@ -104,14 +89,7 @@ impl Sas { /// Cancel the interactive verification flow. pub async fn cancel(&self) -> Result<()> { if let Some(request) = self.inner.cancel() { - match request { - OutgoingVerificationRequest::ToDevice(r) => { - self.client.send_to_device(&r).await?; - } - OutgoingVerificationRequest::InRoom(r) => { - self.client.room_send_helper(&r).await?; - } - } + self.client.send_verification_request(request).await?; } Ok(()) @@ -138,6 +116,11 @@ impl Sas { self.inner.is_done() } + /// Are we in a state where we can show the short auth string. + pub fn can_be_presented(&self) -> bool { + self.inner.can_be_presented() + } + /// Is the verification process canceled. pub fn is_cancelled(&self) -> bool { self.inner.is_cancelled() diff --git a/matrix_sdk/src/verification_request.rs b/matrix_sdk/src/verification_request.rs deleted file mode 100644 index 4d2773a2..00000000 --- a/matrix_sdk/src/verification_request.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use matrix_sdk_base::crypto::{ - OutgoingVerificationRequest, VerificationRequest as BaseVerificationRequest, -}; - -use crate::{Client, Result}; - -/// An object controlling the interactive verification flow. -#[derive(Debug, Clone)] -pub struct VerificationRequest { - pub(crate) inner: BaseVerificationRequest, - pub(crate) client: Client, -} - -impl VerificationRequest { - /// Accept the verification request - pub async fn accept(&self) -> Result<()> { - 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_base/src/client.rs b/matrix_sdk_base/src/client.rs index 0b16beff..b36d9041 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -36,7 +36,7 @@ use matrix_sdk_common::{locks::Mutex, uuid::Uuid}; use matrix_sdk_crypto::{ store::{CryptoStore, CryptoStoreError}, Device, EncryptionSettings, IncomingResponse, MegolmError, OlmError, OlmMachine, - OutgoingRequest, Sas, ToDeviceRequest, UserDevices, + OutgoingRequest, ToDeviceRequest, UserDevices, }; #[cfg(feature = "encryption")] use ruma::{ @@ -1202,26 +1202,6 @@ impl BaseClient { } } - /// Get a `Sas` verification object with the given flow id. - /// - /// # Arguments - /// - /// * `flow_id` - The unique id that identifies a interactive verification - /// flow. For in-room verifications this will be the event id of the - /// *m.key.verification.request* event that started the flow, for the - /// to-device verification flows this will be the transaction id of the - /// *m.key.verification.start* event. - #[cfg(feature = "encryption")] - #[cfg_attr(feature = "docs", doc(cfg(encryption)))] - pub async fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option { - self.olm - .lock() - .await - .as_ref() - .and_then(|o| o.get_verification(user_id, flow_id).map(|v| v.sas_v1())) - .flatten() - } - /// Get a specific device of a user. /// /// # Arguments diff --git a/matrix_sdk_base/src/store/sled_store/mod.rs b/matrix_sdk_base/src/store/sled_store/mod.rs index 4f3ba8f2..31a9ccbd 100644 --- a/matrix_sdk_base/src/store/sled_store/mod.rs +++ b/matrix_sdk_base/src/store/sled_store/mod.rs @@ -720,6 +720,7 @@ impl SledStore { .map(|u| { u.map_err(StoreError::Sled).and_then(|(key, value)| { self.deserialize_event(&value) + // TODO remove this unwrapping .map(|receipt| { (decode_key_value(&key, 3).unwrap().try_into().unwrap(), receipt) }) From 0fb3dedd1cd3b0766fa7378754480d52d38e8ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 17 Jun 2021 12:35:37 +0200 Subject: [PATCH 25/88] client: Fix compilation when the encryption feature is disabled --- matrix_sdk/src/client.rs | 1 + matrix_sdk_base/src/store/memory_store.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 62f04092..2dff600b 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -2702,6 +2702,7 @@ impl Client { self.send(request, None).await } + #[cfg(feature = "encryption")] pub(crate) async fn send_verification_request( &self, request: matrix_sdk_base::crypto::OutgoingVerificationRequest, diff --git a/matrix_sdk_base/src/store/memory_store.rs b/matrix_sdk_base/src/store/memory_store.rs index 8bf11358..627d5b9a 100644 --- a/matrix_sdk_base/src/store/memory_store.rs +++ b/matrix_sdk_base/src/store/memory_store.rs @@ -560,13 +560,13 @@ impl StateStore for MemoryStore { #[cfg(test)] #[cfg(not(feature = "sled_state_store"))] mod test { - use matrix_sdk_common::{ + use matrix_sdk_test::async_test; + use ruma::{ api::client::r0::media::get_content_thumbnail::Method, identifiers::{event_id, mxc_uri, room_id, user_id, UserId}, receipt::ReceiptType, uint, }; - use matrix_sdk_test::async_test; use serde_json::json; use super::{MemoryStore, StateChanges}; From 092ca9040348e101c43a5e08f25c6d751683fdf4 Mon Sep 17 00:00:00 2001 From: SaurusXI Date: Sun, 20 Jun 2021 12:24:40 +0530 Subject: [PATCH 26/88] matrix-sdk: add method to check room's event visibility --- matrix_sdk/src/room/common.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index 22fab1c8..516c234d 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -7,6 +7,7 @@ use ruma::{ membership::{get_member_events, join_room_by_id, leave_room}, message::get_message_events, }, + events::room::history_visibility::HistoryVisibility, UserId, }; @@ -176,6 +177,17 @@ impl Common { Ok(()) } + fn are_events_visible(&self) -> bool { + if let RoomType::Invited = self.inner.room_type() { + match self.inner.history_visibility() { + HistoryVisibility::WorldReadable | HistoryVisibility::Shared => true, + _ => false, + } + } + + true + } + /// Sync the member list with the server. /// /// This method will de-duplicate requests if it is called multiple times in From 0e84349d059c08d5b958013390e1a8ee99968d7c Mon Sep 17 00:00:00 2001 From: SaurusXI Date: Sun, 20 Jun 2021 12:30:12 +0530 Subject: [PATCH 27/88] matrix-sdk: add event visibility check to ensure_members --- matrix_sdk/src/room/common.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index 516c234d..4da317f4 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -13,7 +13,7 @@ use ruma::{ use crate::{ media::{MediaFormat, MediaRequest, MediaType}, - BaseRoom, Client, Result, RoomMember, + BaseRoom, Client, Result, RoomMember, room::RoomType, error::{self, HttpError} }; /// A struct containing methods that are common for Joined, Invited and Left @@ -170,6 +170,12 @@ impl Common { } async fn ensure_members(&self) -> Result<()> { + if !self.are_events_visible() { + return Err( + error::Error::Http(HttpError::NotClientRequest) + ) + } + if !self.are_members_synced() { self.request_members().await?; } @@ -179,7 +185,7 @@ impl Common { fn are_events_visible(&self) -> bool { if let RoomType::Invited = self.inner.room_type() { - match self.inner.history_visibility() { + return match self.inner.history_visibility() { HistoryVisibility::WorldReadable | HistoryVisibility::Shared => true, _ => false, } From bdd35206e8c92aea54285d1b89be9d5e9a49d56f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20H=C3=A4cker?= Date: Sun, 20 Jun 2021 17:04:31 +0200 Subject: [PATCH 28/88] event_handler: Add AnySyncMessageEvent::Reaction --- matrix_sdk/src/event_handler/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/matrix_sdk/src/event_handler/mod.rs b/matrix_sdk/src/event_handler/mod.rs index 391acc79..41a08618 100644 --- a/matrix_sdk/src/event_handler/mod.rs +++ b/matrix_sdk/src/event_handler/mod.rs @@ -27,6 +27,7 @@ use ruma::{ ignored_user_list::IgnoredUserListEventContent, presence::PresenceEvent, push_rules::PushRulesEventContent, + reaction::ReactionEventContent, receipt::ReceiptEventContent, room::{ aliases::AliasesEventContent, @@ -167,6 +168,7 @@ impl Handler { self.on_room_message_feedback(room, e).await } AnySyncMessageEvent::RoomRedaction(e) => self.on_room_redaction(room, e).await, + AnySyncMessageEvent::Reaction(e) => self.on_room_reaction(room, e).await, AnySyncMessageEvent::Custom(e) => { self.on_custom_event(room, &CustomEvent::Message(e)).await } @@ -358,6 +360,8 @@ pub trait EventHandler: Send + Sync { async fn on_room_message(&self, _: Room, _: &SyncMessageEvent) {} /// Fires when `Client` receives a `RoomEvent::RoomMessageFeedback` event. async fn on_room_message_feedback(&self, _: Room, _: &SyncMessageEvent) {} + /// Fires when `Client` receives a `RoomEvent::Reaction` event. + async fn on_room_reaction(&self, _: Room, _: &SyncMessageEvent) {} /// Fires when `Client` receives a `RoomEvent::CallInvite` event async fn on_room_call_invite(&self, _: Room, _: &SyncMessageEvent) {} /// Fires when `Client` receives a `RoomEvent::CallAnswer` event From b984fcca0c8969917405f2d742f2356ba6540b72 Mon Sep 17 00:00:00 2001 From: SaurusXI Date: Mon, 21 Jun 2021 18:06:55 +0530 Subject: [PATCH 29/88] matrix-sdk: (fix) correct the history visibility states that allow us to view events in are_events_visible --- matrix_sdk/src/room/common.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index 4da317f4..02c8583b 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -12,8 +12,10 @@ use ruma::{ }; use crate::{ + error::{self, HttpError}, media::{MediaFormat, MediaRequest, MediaType}, - BaseRoom, Client, Result, RoomMember, room::RoomType, error::{self, HttpError} + room::RoomType, + BaseRoom, Client, Result, RoomMember, }; /// A struct containing methods that are common for Joined, Invited and Left @@ -171,9 +173,7 @@ impl Common { async fn ensure_members(&self) -> Result<()> { if !self.are_events_visible() { - return Err( - error::Error::Http(HttpError::NotClientRequest) - ) + return Err(error::Error::Http(HttpError::NotClientRequest)); } if !self.are_members_synced() { @@ -186,11 +186,11 @@ impl Common { fn are_events_visible(&self) -> bool { if let RoomType::Invited = self.inner.room_type() { return match self.inner.history_visibility() { - HistoryVisibility::WorldReadable | HistoryVisibility::Shared => true, + HistoryVisibility::WorldReadable | HistoryVisibility::Invited => true, _ => false, - } + }; } - + true } From ae5be67322a6a48ea81cca1424c472c033f9c81c Mon Sep 17 00:00:00 2001 From: SaurusXI Date: Mon, 21 Jun 2021 18:28:11 +0530 Subject: [PATCH 30/88] matrix-sdk: (fix) return Ok(()) in ensure_members when returning early --- matrix_sdk/src/room/common.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index 02c8583b..dfc2cf08 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -12,7 +12,6 @@ use ruma::{ }; use crate::{ - error::{self, HttpError}, media::{MediaFormat, MediaRequest, MediaType}, room::RoomType, BaseRoom, Client, Result, RoomMember, @@ -173,7 +172,7 @@ impl Common { async fn ensure_members(&self) -> Result<()> { if !self.are_events_visible() { - return Err(error::Error::Http(HttpError::NotClientRequest)); + return Ok(()); } if !self.are_members_synced() { From 43e213fd67401ca11f0d336e033bd0d48be7b02c Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sun, 20 Jun 2021 01:35:28 +0200 Subject: [PATCH 31/88] matrix-sdk: Update ruma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Damir Jelić --- matrix_qrcode/Cargo.toml | 2 +- matrix_sdk/Cargo.toml | 2 + matrix_sdk/examples/emoji_verification.rs | 4 +- matrix_sdk/src/client.rs | 4 +- matrix_sdk/src/event_handler/mod.rs | 120 ++++++++++++---- matrix_sdk_appservice/Cargo.toml | 2 + matrix_sdk_base/Cargo.toml | 2 +- matrix_sdk_common/Cargo.toml | 2 +- matrix_sdk_crypto/Cargo.toml | 2 +- matrix_sdk_crypto/src/identities/device.rs | 20 +-- matrix_sdk_crypto/src/identities/user.rs | 2 +- matrix_sdk_crypto/src/key_request.rs | 104 ++++++-------- matrix_sdk_crypto/src/machine.rs | 44 +++--- matrix_sdk_crypto/src/olm/account.rs | 15 +- .../src/olm/group_sessions/inbound.rs | 8 +- .../src/olm/group_sessions/outbound.rs | 36 ++--- matrix_sdk_crypto/src/olm/mod.rs | 5 +- matrix_sdk_crypto/src/olm/session.rs | 14 +- matrix_sdk_crypto/src/olm/signing/mod.rs | 6 +- .../src/olm/signing/pk_signing.rs | 3 +- matrix_sdk_crypto/src/requests.rs | 29 ++-- .../src/session_manager/group_sessions.rs | 18 +-- .../src/session_manager/sessions.rs | 33 ++--- matrix_sdk_crypto/src/store/sled.rs | 2 +- .../src/verification/event_enums.rs | 131 +++++++++--------- matrix_sdk_crypto/src/verification/mod.rs | 37 +---- .../src/verification/requests.rs | 14 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 4 +- .../src/verification/sas/sas_state.rs | 64 ++++----- matrix_sdk_test/Cargo.toml | 2 +- 30 files changed, 369 insertions(+), 362 deletions(-) diff --git a/matrix_qrcode/Cargo.toml b/matrix_qrcode/Cargo.toml index 28edd7d6..3932e4bf 100644 --- a/matrix_qrcode/Cargo.toml +++ b/matrix_qrcode/Cargo.toml @@ -20,5 +20,5 @@ byteorder = "1.4.3" image = { version = "0.23.14", optional = true } qrcode = { version = "0.12.0", default-features = false } rqrr = { version = "0.3.2" , optional = true } -ruma-identifiers = "0.19.1" +ruma-identifiers = { version = "0.19.3", git = "https://github.com/ruma/ruma", rev = "d73ab8add" } thiserror = "1.0.24" diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index 86c41c06..a4cec562 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -56,6 +56,8 @@ default_features = false [dependencies.ruma] version = "0.1.2" +git = "https://github.com/ruma/ruma" +rev = "d73ab8add" features = ["client-api-c", "compat", "unstable-pre-spec"] [dependencies.tokio-stream] diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index ac212aec..60e93b6c 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -142,7 +142,7 @@ async fn login( if let Some(Verification::SasV1(sas)) = client .get_verification( &e.sender, - e.content.relation.event_id.as_str(), + e.content.relates_to.event_id.as_str(), ) .await { @@ -153,7 +153,7 @@ async fn login( if let Some(Verification::SasV1(sas)) = client .get_verification( &e.sender, - e.content.relation.event_id.as_str(), + e.content.relates_to.event_id.as_str(), ) .await { diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 2dff600b..21de669f 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -1786,8 +1786,8 @@ impl Client { request: &ToDeviceRequest, ) -> Result { let txn_id_string = request.txn_id_string(); - let request = RumaToDeviceRequest::new( - request.event_type.clone(), + let request = RumaToDeviceRequest::new_raw( + request.event_type.as_str(), &txn_id_string, request.messages.clone(), ); diff --git a/matrix_sdk/src/event_handler/mod.rs b/matrix_sdk/src/event_handler/mod.rs index 41a08618..666bb757 100644 --- a/matrix_sdk/src/event_handler/mod.rs +++ b/matrix_sdk/src/event_handler/mod.rs @@ -47,6 +47,7 @@ use ruma::{ GlobalAccountDataEvent, RoomAccountDataEvent, StrippedStateEvent, SyncEphemeralRoomEvent, SyncMessageEvent, SyncStateEvent, }, + serde::Raw, RoomId, }; use serde_json::value::RawValue as RawJsonValue; @@ -89,14 +90,24 @@ impl Handler { self.handle_room_account_data_event(room.clone(), &event).await; } - for event in room_info.state.events.iter().filter_map(|e| e.deserialize().ok()) { - self.handle_state_event(room.clone(), &event).await; + for (raw_event, event) in room_info.state.events.iter().filter_map(|e| { + if let Ok(d) = e.deserialize() { + Some((e, d)) + } else { + None + } + }) { + self.handle_state_event(room.clone(), &event, raw_event).await; } - for event in - room_info.timeline.events.iter().filter_map(|e| e.event.deserialize().ok()) - { - self.handle_timeline_event(room.clone(), &event).await; + for (raw_event, event) in room_info.timeline.events.iter().filter_map(|e| { + if let Ok(d) = e.event.deserialize() { + Some((&e.event, d)) + } else { + None + } + }) { + self.handle_timeline_event(room.clone(), &event, raw_event).await; } } } @@ -109,14 +120,24 @@ impl Handler { self.handle_room_account_data_event(room.clone(), &event).await; } - for event in room_info.state.events.iter().filter_map(|e| e.deserialize().ok()) { - self.handle_state_event(room.clone(), &event).await; + for (raw_event, event) in room_info.state.events.iter().filter_map(|e| { + if let Ok(d) = e.deserialize() { + Some((e, d)) + } else { + None + } + }) { + self.handle_state_event(room.clone(), &event, raw_event).await; } - for event in - room_info.timeline.events.iter().filter_map(|e| e.event.deserialize().ok()) - { - self.handle_timeline_event(room.clone(), &event).await; + for (raw_event, event) in room_info.timeline.events.iter().filter_map(|e| { + if let Ok(d) = e.event.deserialize() { + Some((&e.event, d)) + } else { + None + } + }) { + self.handle_timeline_event(room.clone(), &event, raw_event).await; } } } @@ -144,7 +165,12 @@ impl Handler { } } - async fn handle_timeline_event(&self, room: Room, event: &AnySyncRoomEvent) { + async fn handle_timeline_event( + &self, + room: Room, + event: &AnySyncRoomEvent, + raw_event: &Raw, + ) { match event { AnySyncRoomEvent::State(event) => match event { AnySyncStateEvent::RoomMember(e) => self.on_room_member(room, e).await, @@ -157,10 +183,25 @@ impl Handler { AnySyncStateEvent::RoomPowerLevels(e) => self.on_room_power_levels(room, e).await, AnySyncStateEvent::RoomTombstone(e) => self.on_room_tombstone(room, e).await, AnySyncStateEvent::RoomJoinRules(e) => self.on_room_join_rules(room, e).await, - AnySyncStateEvent::Custom(e) => { - self.on_custom_event(room, &CustomEvent::State(e)).await + AnySyncStateEvent::PolicyRuleRoom(_) + | AnySyncStateEvent::PolicyRuleServer(_) + | AnySyncStateEvent::PolicyRuleUser(_) + | AnySyncStateEvent::RoomCreate(_) + | AnySyncStateEvent::RoomEncryption(_) + | AnySyncStateEvent::RoomGuestAccess(_) + | AnySyncStateEvent::RoomHistoryVisibility(_) + | AnySyncStateEvent::RoomPinnedEvents(_) + | AnySyncStateEvent::RoomServerAcl(_) + | AnySyncStateEvent::RoomThirdPartyInvite(_) + | AnySyncStateEvent::RoomTopic(_) + | AnySyncStateEvent::SpaceChild(_) + | AnySyncStateEvent::SpaceParent(_) => {} + _ => { + if let Ok(e) = raw_event.deserialize_as::>() + { + self.on_custom_event(room, &CustomEvent::State(&e)).await; + } } - _ => {} }, AnySyncRoomEvent::Message(event) => match event { AnySyncMessageEvent::RoomMessage(e) => self.on_room_message(room, e).await, @@ -169,23 +210,40 @@ impl Handler { } AnySyncMessageEvent::RoomRedaction(e) => self.on_room_redaction(room, e).await, AnySyncMessageEvent::Reaction(e) => self.on_room_reaction(room, e).await, - AnySyncMessageEvent::Custom(e) => { - self.on_custom_event(room, &CustomEvent::Message(e)).await - } AnySyncMessageEvent::CallInvite(e) => self.on_room_call_invite(room, e).await, AnySyncMessageEvent::CallAnswer(e) => self.on_room_call_answer(room, e).await, AnySyncMessageEvent::CallCandidates(e) => { self.on_room_call_candidates(room, e).await } AnySyncMessageEvent::CallHangup(e) => self.on_room_call_hangup(room, e).await, - _ => {} + AnySyncMessageEvent::KeyVerificationReady(_) + | AnySyncMessageEvent::KeyVerificationStart(_) + | AnySyncMessageEvent::KeyVerificationCancel(_) + | AnySyncMessageEvent::KeyVerificationAccept(_) + | AnySyncMessageEvent::KeyVerificationKey(_) + | AnySyncMessageEvent::KeyVerificationMac(_) + | AnySyncMessageEvent::KeyVerificationDone(_) + | AnySyncMessageEvent::RoomEncrypted(_) + | AnySyncMessageEvent::Sticker(_) => {} + _ => { + if let Ok(e) = + raw_event.deserialize_as::>() + { + self.on_custom_event(room, &CustomEvent::Message(&e)).await; + } + } }, AnySyncRoomEvent::RedactedState(_event) => {} AnySyncRoomEvent::RedactedMessage(_event) => {} } } - async fn handle_state_event(&self, room: Room, event: &AnySyncStateEvent) { + async fn handle_state_event( + &self, + room: Room, + event: &AnySyncStateEvent, + raw_event: &Raw, + ) { match event { AnySyncStateEvent::RoomMember(member) => self.on_state_member(room, member).await, AnySyncStateEvent::RoomName(name) => self.on_state_name(room, name).await, @@ -202,10 +260,24 @@ impl Handler { // TODO make `on_state_tombstone` method self.on_room_tombstone(room, tomb).await } - AnySyncStateEvent::Custom(custom) => { - self.on_custom_event(room, &CustomEvent::State(custom)).await + AnySyncStateEvent::PolicyRuleRoom(_) + | AnySyncStateEvent::PolicyRuleServer(_) + | AnySyncStateEvent::PolicyRuleUser(_) + | AnySyncStateEvent::RoomCreate(_) + | AnySyncStateEvent::RoomEncryption(_) + | AnySyncStateEvent::RoomGuestAccess(_) + | AnySyncStateEvent::RoomHistoryVisibility(_) + | AnySyncStateEvent::RoomPinnedEvents(_) + | AnySyncStateEvent::RoomServerAcl(_) + | AnySyncStateEvent::RoomThirdPartyInvite(_) + | AnySyncStateEvent::RoomTopic(_) + | AnySyncStateEvent::SpaceChild(_) + | AnySyncStateEvent::SpaceParent(_) => {} + _ => { + if let Ok(e) = raw_event.deserialize_as::>() { + self.on_custom_event(room, &CustomEvent::State(&e)).await; + } } - _ => {} } } diff --git a/matrix_sdk_appservice/Cargo.toml b/matrix_sdk_appservice/Cargo.toml index f14096c2..4a21b3df 100644 --- a/matrix_sdk_appservice/Cargo.toml +++ b/matrix_sdk_appservice/Cargo.toml @@ -33,6 +33,8 @@ matrix-sdk = { version = "0.2", path = "../matrix_sdk", default-features = false [dependencies.ruma] version = "0.1.2" +git = "https://github.com/ruma/ruma" +rev = "d73ab8add" features = ["client-api-c", "appservice-api-s", "unstable-pre-spec"] [dev-dependencies] diff --git a/matrix_sdk_base/Cargo.toml b/matrix_sdk_base/Cargo.toml index 2adcac7a..10af9343 100644 --- a/matrix_sdk_base/Cargo.toml +++ b/matrix_sdk_base/Cargo.toml @@ -26,7 +26,7 @@ docs = ["encryption", "sled_cryptostore"] [dependencies] dashmap = "4.0.2" lru = "0.6.5" -ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"] } +ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } serde = { version = "1.0.122", features = ["rc"] } serde_json = "1.0.61" tracing = "0.1.22" diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index 4839c98a..3eb20b6d 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -13,7 +13,7 @@ version = "0.2.0" [dependencies] async-trait = "0.1.42" instant = { version = "0.1.9", features = ["wasm-bindgen", "now"] } -ruma = { version = "0.1.2", features = ["client-api-c"] } +ruma = { version = "0.1.2", features = ["client-api-c"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } serde = "1.0.122" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index bbad56e7..cfa3c4da 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -22,7 +22,7 @@ docs = ["sled_cryptostore"] [dependencies] matrix-qrcode = { version = "0.1.0", path = "../matrix_qrcode" } matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } -ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"] } +ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } olm-rs = { version = "1.0.0", features = ["serde"] } getrandom = "0.2.2" diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 0fef3a7a..f57e62ff 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -25,11 +25,10 @@ use std::{ use atomic::Atomic; use matrix_sdk_common::locks::Mutex; use ruma::{ - api::client::r0::keys::SignedKey, - encryption::DeviceKeys, + encryption::{DeviceKeys, SignedKey}, events::{ forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, - room::encrypted::EncryptedEventContent, EventType, + room::encrypted::EncryptedEventContent, AnyToDeviceEventContent, }, identifiers::{ DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId, @@ -176,15 +175,12 @@ impl Device { /// /// # Arguments /// - /// * `event_type` - The type of the event. - /// /// * `content` - The content of the event that should be encrypted. pub(crate) async fn encrypt( &self, - event_type: EventType, - content: Value, + content: AnyToDeviceEventContent, ) -> OlmResult<(Session, EncryptedEventContent)> { - self.inner.encrypt(&*self.verification_machine.store, event_type, content).await + self.inner.encrypt(&*self.verification_machine.store, content).await } /// Encrypt the given inbound group session as a forwarded room key for this @@ -213,8 +209,7 @@ impl Device { ); }; - let content = serde_json::to_value(content)?; - self.encrypt(EventType::ForwardedRoomKey, content).await + self.encrypt(AnyToDeviceEventContent::ForwardedRoomKey(content)).await } } @@ -421,8 +416,7 @@ impl ReadOnlyDevice { pub(crate) async fn encrypt( &self, store: &dyn CryptoStore, - event_type: EventType, - content: Value, + content: AnyToDeviceEventContent, ) -> OlmResult<(Session, EncryptedEventContent)> { let sender_key = if let Some(k) = self.get_key(DeviceKeyAlgorithm::Curve25519) { k @@ -455,7 +449,7 @@ impl ReadOnlyDevice { return Err(OlmError::MissingSession); }; - let message = session.encrypt(self, event_type, content).await?; + let message = session.encrypt(self, content).await?; Ok((session, message)) } diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index 2015e56c..e983f786 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -22,7 +22,7 @@ use std::{ }; use ruma::{ - api::client::r0::keys::{CrossSigningKey, KeyUsage}, + encryption::{CrossSigningKey, KeyUsage}, DeviceKeyId, UserId, }; use serde::{Deserialize, Serialize}; diff --git a/matrix_sdk_crypto/src/key_request.rs b/matrix_sdk_crypto/src/key_request.rs index 9732d8d4..ba3d1287 100644 --- a/matrix_sdk_crypto/src/key_request.rs +++ b/matrix_sdk_crypto/src/key_request.rs @@ -20,21 +20,20 @@ // If we don't trust the device store an object that remembers the request and // let the users introspect that object. -use std::{collections::BTreeMap, sync::Arc}; +use std::sync::Arc; use dashmap::{mapref::entry::Entry, DashMap, DashSet}; use matrix_sdk_common::uuid::Uuid; use ruma::{ - api::client::r0::to_device::DeviceIdOrAllDevices, events::{ forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, room_key_request::{Action, RequestedKeyInfo, RoomKeyRequestToDeviceEventContent}, - AnyToDeviceEvent, EventType, ToDeviceEvent, + AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, identifiers::{DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId}, + to_device::DeviceIdOrAllDevices, }; use serde::{Deserialize, Serialize}; -use serde_json::value::to_raw_value; use thiserror::Error; use tracing::{error, info, trace, warn}; @@ -150,7 +149,7 @@ pub struct OutgoingKeyRequest { } impl OutgoingKeyRequest { - fn to_request(&self, own_device_id: &DeviceId) -> Result { + fn to_request(&self, own_device_id: &DeviceId) -> OutgoingRequest { let content = RoomKeyRequestToDeviceEventContent::new( Action::Request, Some(self.info.clone()), @@ -158,13 +157,17 @@ impl OutgoingKeyRequest { self.request_id.to_string(), ); - wrap_key_request_content(self.request_recipient.clone(), self.request_id, &content) + let request = ToDeviceRequest::new_with_id( + &self.request_recipient, + DeviceIdOrAllDevices::AllDevices, + AnyToDeviceEventContent::RoomKeyRequest(content), + self.request_id, + ); + + OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) } } - fn to_cancellation( - &self, - own_device_id: &DeviceId, - ) -> Result { + fn to_cancellation(&self, own_device_id: &DeviceId) -> OutgoingRequest { let content = RoomKeyRequestToDeviceEventContent::new( Action::CancelRequest, None, @@ -172,8 +175,13 @@ impl OutgoingKeyRequest { self.request_id.to_string(), ); - let id = Uuid::new_v4(); - wrap_key_request_content(self.request_recipient.clone(), id, &content) + let request = ToDeviceRequest::new( + &self.request_recipient, + DeviceIdOrAllDevices::AllDevices, + AnyToDeviceEventContent::RoomKeyRequest(content), + ); + + OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) } } } @@ -187,26 +195,6 @@ impl PartialEq for OutgoingKeyRequest { } } -fn wrap_key_request_content( - recipient: UserId, - id: Uuid, - content: &RoomKeyRequestToDeviceEventContent, -) -> Result { - let mut messages = BTreeMap::new(); - - messages - .entry(recipient) - .or_insert_with(BTreeMap::new) - .insert(DeviceIdOrAllDevices::AllDevices, to_raw_value(content)?); - - Ok(OutgoingRequest { - request_id: id, - request: Arc::new( - ToDeviceRequest { event_type: EventType::RoomKeyRequest, txn_id: id, messages }.into(), - ), - }) -} - impl KeyRequestMachine { pub fn new( user_id: Arc, @@ -229,13 +217,14 @@ impl KeyRequestMachine { /// Load stored outgoing requests that were not yet sent out. async fn load_outgoing_requests(&self) -> Result, CryptoStoreError> { - self.store + Ok(self + .store .get_unsent_key_requests() .await? .into_iter() .filter(|i| !i.sent_out) - .map(|info| info.to_request(self.device_id()).map_err(CryptoStoreError::from)) - .collect() + .map(|info| info.to_request(self.device_id())) + .collect()) } /// Our own user id. @@ -448,23 +437,15 @@ impl KeyRequestMachine { let (used_session, content) = device.encrypt_session(session.clone(), message_index).await?; - let id = Uuid::new_v4(); - let mut messages = BTreeMap::new(); - - messages.entry(device.user_id().to_owned()).or_insert_with(BTreeMap::new).insert( - DeviceIdOrAllDevices::DeviceId(device.device_id().into()), - to_raw_value(&content)?, + let request = ToDeviceRequest::new( + device.user_id(), + device.device_id().to_owned(), + AnyToDeviceEventContent::RoomEncrypted(content), ); - let request = OutgoingRequest { - request_id: id, - request: Arc::new( - ToDeviceRequest { event_type: EventType::RoomEncrypted, txn_id: id, messages } - .into(), - ), - }; - - self.outgoing_to_device_requests.insert(id, request); + let request = + OutgoingRequest { request_id: request.txn_id, request: Arc::new(request.into()) }; + self.outgoing_to_device_requests.insert(request.request_id, request); Ok(used_session) } @@ -594,8 +575,8 @@ impl KeyRequestMachine { let request = self.store.get_key_request_by_info(&key_info).await?; if let Some(request) = request { - let cancel = request.to_cancellation(self.device_id())?; - let request = request.to_request(self.device_id())?; + let cancel = request.to_cancellation(self.device_id()); + let request = request.to_request(self.device_id()); Ok((Some(cancel), request)) } else { @@ -618,7 +599,7 @@ impl KeyRequestMachine { sent_out: false, }; - let outgoing_request = request.to_request(self.device_id())?; + let outgoing_request = request.to_request(self.device_id()); self.save_outgoing_key_info(request).await?; Ok(outgoing_request) @@ -717,7 +698,7 @@ impl KeyRequestMachine { // can delete it in one transaction. self.delete_key_info(&key_info).await?; - let request = key_info.to_cancellation(self.device_id())?; + let request = key_info.to_cancellation(self.device_id()); self.outgoing_to_device_requests.insert(request.request_id, request); Ok(()) @@ -789,13 +770,14 @@ mod test { use matrix_sdk_common::locks::Mutex; use matrix_sdk_test::async_test; use ruma::{ - api::client::r0::to_device::DeviceIdOrAllDevices, events::{ forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, room::encrypted::EncryptedEventContent, room_key_request::RoomKeyRequestToDeviceEventContent, AnyToDeviceEvent, ToDeviceEvent, }, - room_id, user_id, DeviceIdBox, RoomId, UserId, + room_id, + to_device::DeviceIdOrAllDevices, + user_id, DeviceIdBox, RoomId, UserId, }; use super::{KeyRequestMachine, KeyshareDecision}; @@ -1203,8 +1185,7 @@ mod test { .unwrap() .get(&DeviceIdOrAllDevices::AllDevices) .unwrap(); - let content: RoomKeyRequestToDeviceEventContent = - serde_json::from_str(content.get()).unwrap(); + let content: RoomKeyRequestToDeviceEventContent = content.deserialize_as().unwrap(); alice_machine.mark_outgoing_request_as_sent(id).await.unwrap(); @@ -1233,7 +1214,7 @@ mod test { .unwrap() .get(&DeviceIdOrAllDevices::DeviceId(alice_device_id())) .unwrap(); - let content: EncryptedEventContent = serde_json::from_str(content.get()).unwrap(); + let content: EncryptedEventContent = content.deserialize_as().unwrap(); bob_machine.mark_outgoing_request_as_sent(id).await.unwrap(); @@ -1336,8 +1317,7 @@ mod test { .unwrap() .get(&DeviceIdOrAllDevices::AllDevices) .unwrap(); - let content: RoomKeyRequestToDeviceEventContent = - serde_json::from_str(content.get()).unwrap(); + let content: RoomKeyRequestToDeviceEventContent = content.deserialize_as().unwrap(); alice_machine.mark_outgoing_request_as_sent(id).await.unwrap(); @@ -1382,7 +1362,7 @@ mod test { .unwrap() .get(&DeviceIdOrAllDevices::DeviceId(alice_device_id())) .unwrap(); - let content: EncryptedEventContent = serde_json::from_str(content.get()).unwrap(); + let content: EncryptedEventContent = content.deserialize_as().unwrap(); bob_machine.mark_outgoing_request_as_sent(id).await.unwrap(); diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index a22e7929..46763f5a 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -755,11 +755,11 @@ impl OlmMachine { | AnyToDeviceEvent::KeyVerificationStart(..) => { self.handle_verification_event(event).await; } - AnyToDeviceEvent::Dummy(_) => {} - AnyToDeviceEvent::RoomKey(_) => {} - AnyToDeviceEvent::ForwardedRoomKey(_) => {} - AnyToDeviceEvent::RoomEncrypted(_) => {} - AnyToDeviceEvent::Custom(_) => {} + AnyToDeviceEvent::Dummy(_) + | AnyToDeviceEvent::RoomKey(_) + | AnyToDeviceEvent::ForwardedRoomKey(_) + | AnyToDeviceEvent::RoomEncrypted(_) => {} + _ => {} } } @@ -1231,21 +1231,22 @@ pub(crate) mod test { use matrix_sdk_test::test_json; use ruma::{ api::{ - client::r0::keys::{claim_keys, get_keys, upload_keys, OneTimeKey}, + client::r0::keys::{claim_keys, get_keys, upload_keys}, IncomingResponse, }, + encryption::OneTimeKey, events::{ + dummy::DummyToDeviceEventContent, room::{ encrypted::EncryptedEventContent, message::{MessageEventContent, MessageType}, }, AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent, - EventType, SyncMessageEvent, ToDeviceEvent, Unsigned, + AnyToDeviceEventContent, SyncMessageEvent, ToDeviceEvent, Unsigned, }, identifiers::{ event_id, room_id, user_id, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId, }, - serde::Raw, uint, MilliSecondsSinceUnixEpoch, }; use serde_json::json; @@ -1291,12 +1292,16 @@ pub(crate) mod test { fn to_device_requests_to_content(requests: Vec>) -> EncryptedEventContent { let to_device_request = &requests[0]; - let content: Raw = serde_json::from_str( - to_device_request.messages.values().next().unwrap().values().next().unwrap().get(), - ) - .unwrap(); - - content.deserialize().unwrap() + to_device_request + .messages + .values() + .next() + .unwrap() + .values() + .next() + .unwrap() + .deserialize_as() + .unwrap() } pub(crate) async fn get_prepared_machine() -> (OlmMachine, OneTimeKeys) { @@ -1358,7 +1363,10 @@ pub(crate) mod test { let bob_device = alice.get_device(&bob.user_id, &bob.device_id).await.unwrap().unwrap(); - let (session, content) = bob_device.encrypt(EventType::Dummy, json!({})).await.unwrap(); + let (session, content) = bob_device + .encrypt(AnyToDeviceEventContent::Dummy(DummyToDeviceEventContent::new())) + .await + .unwrap(); alice.store.save_sessions(&[session]).await.unwrap(); let event = ToDeviceEvent { sender: alice.user_id().clone(), content }; @@ -1593,7 +1601,11 @@ pub(crate) mod test { let event = ToDeviceEvent { sender: alice.user_id().clone(), - content: bob_device.encrypt(EventType::Dummy, json!({})).await.unwrap().1, + content: bob_device + .encrypt(AnyToDeviceEventContent::Dummy(DummyToDeviceEventContent::new())) + .await + .unwrap() + .1, }; let event = bob.decrypt_to_device_event(&event).await.unwrap().event.deserialize().unwrap(); diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 2ed7750b..718080fb 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -30,13 +30,9 @@ use olm_rs::{ session::{OlmMessage, PreKeyMessage}, PicklingMode, }; -#[cfg(test)] -use ruma::events::EventType; use ruma::{ - api::client::r0::keys::{ - upload_keys, upload_signatures::Request as SignatureUploadRequest, OneTimeKey, SignedKey, - }, - encryption::DeviceKeys, + api::client::r0::keys::{upload_keys, upload_signatures::Request as SignatureUploadRequest}, + encryption::{DeviceKeys, OneTimeKey, SignedKey}, events::{ room::encrypted::{EncryptedEventContent, EncryptedEventScheme}, AnyToDeviceEvent, ToDeviceEvent, @@ -993,6 +989,8 @@ impl ReadOnlyAccount { #[cfg(test)] pub(crate) async fn create_session_for(&self, other: &ReadOnlyAccount) -> (Session, Session) { + use ruma::events::{dummy::DummyToDeviceEventContent, AnyToDeviceEventContent}; + other.generate_one_time_keys_helper(1).await; let one_time = other.signed_one_time_keys().await.unwrap(); @@ -1003,7 +1001,10 @@ impl ReadOnlyAccount { other.mark_keys_as_published().await; - let message = our_session.encrypt(&device, EventType::Dummy, json!({})).await.unwrap(); + let message = our_session + .encrypt(&device, AnyToDeviceEventContent::Dummy(DummyToDeviceEventContent::new())) + .await + .unwrap(); let content = if let EncryptedEventScheme::OlmV1Curve25519AesSha2(c) = message.scheme { c } else { diff --git a/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs b/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs index e557d585..75b6529b 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs @@ -320,11 +320,9 @@ impl InboundGroupSession { decrypted_object.get_mut("content").map(|c| c.as_object_mut()).flatten() { if !decrypted_content.contains_key("m.relates_to") { - if let Some(relation) = &event.content.relates_to { - decrypted_content.insert( - "m.relates_to".to_owned(), - serde_json::to_value(relation).unwrap_or_default(), - ); + let content = serde_json::to_value(&event.content)?; + if let Some(relation) = content.as_object().and_then(|o| o.get("m.relates_to")) { + decrypted_content.insert("m.relates_to".to_owned(), relation.to_owned()); } } } diff --git a/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs b/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs index fc24d369..b432e44e 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions/outbound.rs @@ -34,20 +34,20 @@ use olm_rs::{ errors::OlmGroupSessionError, outbound_group_session::OlmOutboundGroupSession, PicklingMode, }; use ruma::{ - api::client::r0::to_device::DeviceIdOrAllDevices, events::{ room::{ encrypted::{EncryptedEventContent, EncryptedEventScheme, MegolmV1AesSha2ContentInit}, encryption::EncryptionEventContent, history_visibility::HistoryVisibility, - message::Relation, }, - AnyMessageEventContent, EventContent, + room_key::RoomKeyToDeviceEventContent, + AnyMessageEventContent, AnyToDeviceEventContent, EventContent, }, + to_device::DeviceIdOrAllDevices, DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId, }; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::json; use tracing::{debug, error, trace}; use super::{ @@ -277,13 +277,8 @@ impl OutboundGroupSession { "type": content.event_type(), }); - let relates_to: Option = json_content - .get("content") - .map(|c| c.get("m.relates_to").cloned().map(|r| serde_json::from_value(r).ok())) - .flatten() - .flatten(); - let plaintext = json_content.to_string(); + let relation = content.relation(); let ciphertext = self.encrypt_helper(plaintext).await; @@ -297,7 +292,7 @@ impl OutboundGroupSession { EncryptedEventContent::new( EncryptedEventScheme::MegolmV1AesSha2(encrypted_content), - relates_to, + relation, ) } @@ -361,16 +356,15 @@ impl OutboundGroupSession { session.session_message_index() } - /// Get the outbound group session key as a json value that can be sent as a - /// m.room_key. - pub async fn as_json(&self) -> Value { - json!({ - "algorithm": EventEncryptionAlgorithm::MegolmV1AesSha2, - "room_id": &*self.room_id, - "session_id": &*self.session_id, - "session_key": self.session_key().await, - "chain_index": self.message_index().await, - }) + pub(crate) async fn as_content(&self) -> AnyToDeviceEventContent { + let session_key = self.session_key().await; + + AnyToDeviceEventContent::RoomKey(RoomKeyToDeviceEventContent::new( + EventEncryptionAlgorithm::MegolmV1AesSha2, + self.room_id().to_owned(), + self.session_id().to_owned(), + session_key.0.clone(), + )) } /// Has or will the session be shared with the given user/device pair. diff --git a/matrix_sdk_crypto/src/olm/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index e6fb4743..02767025 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -63,9 +63,8 @@ pub(crate) mod test { use olm_rs::session::OlmMessage; use ruma::{ - api::client::r0::keys::SignedKey, - events::forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, room_id, user_id, - DeviceId, UserId, + encryption::SignedKey, events::forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, + room_id, user_id, DeviceId, UserId, }; use crate::olm::{InboundGroupSession, ReadOnlyAccount, Session}; diff --git a/matrix_sdk_crypto/src/olm/session.rs b/matrix_sdk_crypto/src/olm/session.rs index be34a376..10fdd5ca 100644 --- a/matrix_sdk_crypto/src/olm/session.rs +++ b/matrix_sdk_crypto/src/olm/session.rs @@ -26,12 +26,12 @@ use ruma::{ CiphertextInfo, EncryptedEventContent, EncryptedEventScheme, OlmV1Curve25519AesSha2Content, }, - EventType, + AnyToDeviceEventContent, EventContent, }, identifiers::{DeviceId, DeviceKeyAlgorithm, UserId}, }; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use serde_json::json; use super::{deserialize_instant, serialize_instant, IdentityKeys}; use crate::{ @@ -105,21 +105,17 @@ impl Session { /// encrypted, this needs to be the device that was used to create this /// session with. /// - /// * `event_type` - The type of the event. - /// /// * `content` - The content of the event. pub async fn encrypt( &mut self, recipient_device: &ReadOnlyDevice, - event_type: EventType, - content: Value, + content: AnyToDeviceEventContent, ) -> OlmResult { let recipient_signing_key = recipient_device .get_key(DeviceKeyAlgorithm::Ed25519) .ok_or(EventError::MissingSigningKey)?; - let relates_to = - content.get("m.relates_to").cloned().and_then(|v| serde_json::from_value(v).ok()); + let event_type = content.event_type(); let payload = json!({ "sender": self.user_id.as_str(), @@ -149,7 +145,7 @@ impl Session { content, self.our_identity_keys.curve25519().to_string(), )), - relates_to, + None, )) } diff --git a/matrix_sdk_crypto/src/olm/signing/mod.rs b/matrix_sdk_crypto/src/olm/signing/mod.rs index 3f28b6ad..523c274b 100644 --- a/matrix_sdk_crypto/src/olm/signing/mod.rs +++ b/matrix_sdk_crypto/src/olm/signing/mod.rs @@ -25,8 +25,8 @@ use std::{ use matrix_sdk_common::locks::Mutex; use pk_signing::{MasterSigning, PickledSignings, SelfSigning, Signing, SigningError, UserSigning}; use ruma::{ - api::client::r0::keys::{upload_signatures::Request as SignatureUploadRequest, KeyUsage}, - encryption::DeviceKeys, + api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest, + encryption::{DeviceKeys, KeyUsage}, DeviceKeyAlgorithm, DeviceKeyId, UserId, }; use serde::{Deserialize, Serialize}; @@ -403,7 +403,7 @@ mod test { use std::{collections::BTreeMap, sync::Arc}; use matrix_sdk_test::async_test; - use ruma::{api::client::r0::keys::CrossSigningKey, user_id, UserId}; + use ruma::{encryption::CrossSigningKey, user_id, UserId}; use super::{PrivateCrossSigningIdentity, Signing}; use crate::{ diff --git a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs index 0da882d3..00589e6d 100644 --- a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs +++ b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs @@ -24,8 +24,7 @@ use olm_rs::pk::OlmPkSigning; #[cfg(test)] use olm_rs::{errors::OlmUtilityError, utility::OlmUtility}; use ruma::{ - api::client::r0::keys::{CrossSigningKey, KeyUsage}, - encryption::DeviceKeys, + encryption::{CrossSigningKey, DeviceKeys, KeyUsage}, serde::CanonicalJsonValue, DeviceKeyAlgorithm, DeviceKeyId, UserId, }; diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index 318e8000..428edaef 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -25,16 +25,17 @@ use ruma::{ Request as SignatureUploadRequest, Response as SignatureUploadResponse, }, upload_signing_keys::Response as SigningKeysUploadResponse, - CrossSigningKey, }, message::send_message_event::Response as RoomMessageResponse, - to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices}, + to_device::send_event_to_device::Response as ToDeviceResponse, }, + encryption::CrossSigningKey, events::{AnyMessageEventContent, AnyToDeviceEventContent, EventContent, EventType}, + serde::Raw, + to_device::DeviceIdOrAllDevices, DeviceIdBox, RoomId, UserId, }; use serde::{Deserialize, Serialize}; -use serde_json::value::RawValue as RawJsonValue; /// Customized version of /// `ruma_client_api::r0::to_device::send_event_to_device::Request`, @@ -54,7 +55,7 @@ pub struct ToDeviceRequest { /// The content's type for this field will be updated in a future /// release, until then you can create a value using /// `serde_json::value::to_raw_value`. - pub messages: BTreeMap>>, + pub messages: BTreeMap>>, } impl ToDeviceRequest { @@ -73,18 +74,24 @@ impl ToDeviceRequest { recipient: &UserId, recipient_device: impl Into, content: AnyToDeviceEventContent, + ) -> Self { + Self::new_with_id(recipient, recipient_device, content, Uuid::new_v4()) + } + + pub(crate) fn new_with_id( + recipient: &UserId, + recipient_device: impl Into, + content: AnyToDeviceEventContent, + txn_id: Uuid, ) -> Self { let mut messages = BTreeMap::new(); let mut user_messages = BTreeMap::new(); - - user_messages.insert( - recipient_device.into(), - serde_json::value::to_raw_value(&content).expect("Can't serialize to-device content"), - ); - messages.insert(recipient.clone(), user_messages); let event_type = EventType::from(content.event_type()); - ToDeviceRequest { txn_id: Uuid::new_v4(), event_type, messages } + user_messages.insert(recipient_device.into(), Raw::from(content)); + messages.insert(recipient.clone(), user_messages); + + ToDeviceRequest { event_type, txn_id, messages } } /// Gets the transaction ID as a string. diff --git a/matrix_sdk_crypto/src/session_manager/group_sessions.rs b/matrix_sdk_crypto/src/session_manager/group_sessions.rs index 5d084a4c..f4b210a6 100644 --- a/matrix_sdk_crypto/src/session_manager/group_sessions.rs +++ b/matrix_sdk_crypto/src/session_manager/group_sessions.rs @@ -21,14 +21,14 @@ use dashmap::DashMap; use futures::future::join_all; use matrix_sdk_common::{executor::spawn, uuid::Uuid}; use ruma::{ - api::client::r0::to_device::DeviceIdOrAllDevices, events::{ room::{encrypted::EncryptedEventContent, history_visibility::HistoryVisibility}, - AnyMessageEventContent, EventType, + AnyMessageEventContent, AnyToDeviceEventContent, EventType, }, + serde::Raw, + to_device::DeviceIdOrAllDevices, DeviceId, DeviceIdBox, RoomId, UserId, }; -use serde_json::Value; use tracing::{debug, info, trace}; use crate::{ @@ -224,22 +224,22 @@ impl GroupSessionManager { /// Encrypt the given content for the given devices and create a to-device /// requests that sends the encrypted content to them. async fn encrypt_session_for( - content: Value, + content: AnyToDeviceEventContent, devices: Vec, ) -> OlmResult<(Uuid, ToDeviceRequest, Vec)> { let mut messages = BTreeMap::new(); let mut changed_sessions = Vec::new(); - let encrypt = |device: Device, content: Value| async move { + let encrypt = |device: Device, content: AnyToDeviceEventContent| async move { let mut message = BTreeMap::new(); - let encrypted = device.encrypt(EventType::RoomKey, content.clone()).await; + let encrypted = device.encrypt(content.clone()).await; let used_session = match encrypted { Ok((session, encrypted)) => { message.entry(device.user_id().clone()).or_insert_with(BTreeMap::new).insert( DeviceIdOrAllDevices::DeviceId(device.device_id().into()), - serde_json::value::to_raw_value(&encrypted)?, + Raw::from(AnyToDeviceEventContent::RoomEncrypted(encrypted)), ); Some(session) } @@ -375,7 +375,7 @@ impl GroupSessionManager { pub async fn encrypt_request( chunk: Vec, - content: Value, + content: AnyToDeviceEventContent, outbound: OutboundGroupSession, message_index: u32, being_shared: Arc>, @@ -466,7 +466,7 @@ impl GroupSessionManager { .flatten() .collect(); - let key_content = outbound.as_json().await; + let key_content = outbound.as_content().await; let message_index = outbound.message_index().await; if !devices.is_empty() { diff --git a/matrix_sdk_crypto/src/session_manager/sessions.rs b/matrix_sdk_crypto/src/session_manager/sessions.rs index 657de170..3b742e7f 100644 --- a/matrix_sdk_crypto/src/session_manager/sessions.rs +++ b/matrix_sdk_crypto/src/session_manager/sessions.rs @@ -17,15 +17,13 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; use dashmap::{DashMap, DashSet}; use matrix_sdk_common::uuid::Uuid; use ruma::{ - api::client::r0::{ - keys::claim_keys::{Request as KeysClaimRequest, Response as KeysClaimResponse}, - to_device::DeviceIdOrAllDevices, + api::client::r0::keys::claim_keys::{ + Request as KeysClaimRequest, Response as KeysClaimResponse, }, assign, - events::EventType, + events::{dummy::DummyToDeviceEventContent, AnyToDeviceEventContent}, DeviceId, DeviceIdBox, DeviceKeyAlgorithm, UserId, }; -use serde_json::{json, value::to_raw_value}; use tracing::{error, info, warn}; use crate::{ @@ -118,28 +116,21 @@ impl SessionManager { async fn check_if_unwedged(&self, user_id: &UserId, device_id: &DeviceId) -> OlmResult<()> { if self.wedged_devices.get(user_id).map(|d| d.remove(device_id)).flatten().is_some() { if let Some(device) = self.store.get_device(user_id, device_id).await? { - let (_, content) = device.encrypt(EventType::Dummy, json!({})).await?; - let id = Uuid::new_v4(); - let mut messages = BTreeMap::new(); + let content = AnyToDeviceEventContent::Dummy(DummyToDeviceEventContent::new()); + let (_, content) = device.encrypt(content).await?; - messages.entry(device.user_id().to_owned()).or_insert_with(BTreeMap::new).insert( - DeviceIdOrAllDevices::DeviceId(device.device_id().into()), - to_raw_value(&content)?, + let request = ToDeviceRequest::new( + device.user_id(), + device.device_id().to_owned(), + AnyToDeviceEventContent::RoomEncrypted(content), ); let request = OutgoingRequest { - request_id: id, - request: Arc::new( - ToDeviceRequest { - event_type: EventType::RoomEncrypted, - txn_id: id, - messages, - } - .into(), - ), + request_id: request.txn_id, + request: Arc::new(request.into()), }; - self.outgoing_to_device_requests.insert(id, request); + self.outgoing_to_device_requests.insert(request.request_id, request); } } diff --git a/matrix_sdk_crypto/src/store/sled.rs b/matrix_sdk_crypto/src/store/sled.rs index afa41a05..8dd547c7 100644 --- a/matrix_sdk_crypto/src/store/sled.rs +++ b/matrix_sdk_crypto/src/store/sled.rs @@ -757,7 +757,7 @@ mod test { use matrix_sdk_test::async_test; use olm_rs::outbound_group_session::OlmOutboundGroupSession; use ruma::{ - api::client::r0::keys::SignedKey, + encryption::SignedKey, events::room_key_request::RequestedKeyInfo, identifiers::{room_id, user_id, DeviceId, EventEncryptionAlgorithm, UserId}, }; diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index a8805d70..2c28feb7 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -79,8 +79,7 @@ impl AnyEvent<'_> { | AnyMessageEvent::RoomEncrypted(_) | AnyMessageEvent::RoomMessageFeedback(_) | AnyMessageEvent::RoomRedaction(_) - | AnyMessageEvent::Sticker(_) - | AnyMessageEvent::Custom(_) => None, + | AnyMessageEvent::Sticker(_) => None, AnyMessageEvent::RoomMessage(m) => { if let MessageType::VerificationRequest(v) = &m.content.msgtype { Some(RequestContent::from(v).into()) @@ -105,14 +104,14 @@ impl AnyEvent<'_> { AnyMessageEvent::KeyVerificationDone(e) => { Some(DoneContent::from(&e.content).into()) } + _ => None, }, AnyEvent::ToDevice(e) => match e { AnyToDeviceEvent::Dummy(_) | AnyToDeviceEvent::RoomKey(_) | AnyToDeviceEvent::RoomKeyRequest(_) | AnyToDeviceEvent::ForwardedRoomKey(_) - | AnyToDeviceEvent::RoomEncrypted(_) - | AnyToDeviceEvent::Custom(_) => None, + | AnyToDeviceEvent::RoomEncrypted(_) => None, AnyToDeviceEvent::KeyVerificationRequest(e) => { Some(RequestContent::from(&e.content).into()) } @@ -137,6 +136,7 @@ impl AnyEvent<'_> { AnyToDeviceEvent::KeyVerificationDone(e) => { Some(DoneContent::from(&e.content).into()) } + _ => None, }, } } @@ -178,30 +178,30 @@ impl TryFrom<&AnyMessageEvent> for FlowId { | AnyMessageEvent::RoomEncrypted(_) | AnyMessageEvent::RoomMessageFeedback(_) | AnyMessageEvent::RoomRedaction(_) - | AnyMessageEvent::Sticker(_) - | AnyMessageEvent::Custom(_) => Err(()), + | AnyMessageEvent::Sticker(_) => Err(()), AnyMessageEvent::KeyVerificationReady(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } AnyMessageEvent::RoomMessage(e) => Ok(FlowId::from((&e.room_id, &e.event_id))), AnyMessageEvent::KeyVerificationStart(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } AnyMessageEvent::KeyVerificationCancel(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } AnyMessageEvent::KeyVerificationAccept(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } AnyMessageEvent::KeyVerificationKey(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } AnyMessageEvent::KeyVerificationMac(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } AnyMessageEvent::KeyVerificationDone(e) => { - Ok(FlowId::from((&e.room_id, &e.content.relation.event_id))) + Ok(FlowId::from((&e.room_id, &e.content.relates_to.event_id))) } + _ => Err(()), } } } @@ -215,8 +215,7 @@ impl TryFrom<&AnyToDeviceEvent> for FlowId { | AnyToDeviceEvent::RoomKey(_) | AnyToDeviceEvent::RoomKeyRequest(_) | AnyToDeviceEvent::ForwardedRoomKey(_) - | AnyToDeviceEvent::RoomEncrypted(_) - | AnyToDeviceEvent::Custom(_) => Err(()), + | AnyToDeviceEvent::RoomEncrypted(_) => Err(()), AnyToDeviceEvent::KeyVerificationRequest(e) => { Ok(FlowId::from(e.content.transaction_id.to_owned())) } @@ -241,6 +240,7 @@ impl TryFrom<&AnyToDeviceEvent> for FlowId { AnyToDeviceEvent::KeyVerificationDone(e) => { Ok(FlowId::from(e.content.transaction_id.to_owned())) } + _ => Err(()), } } } @@ -397,7 +397,7 @@ impl<'a> StartContent<'a> { pub fn flow_id(&self) -> &str { match self { Self::ToDevice(c) => &c.transaction_id, - Self::Room(c) => c.relation.event_id.as_str(), + Self::Room(c) => c.relates_to.event_id.as_str(), } } @@ -449,7 +449,7 @@ impl<'a> DoneContent<'a> { pub fn flow_id(&self) -> &str { match self { Self::ToDevice(c) => &c.transaction_id, - Self::Room(c) => c.relation.event_id.as_str(), + Self::Room(c) => c.relates_to.event_id.as_str(), } } } @@ -464,7 +464,7 @@ impl AcceptContent<'_> { pub fn flow_id(&self) -> &str { match self { Self::ToDevice(c) => &c.transaction_id, - Self::Room(c) => c.relation.event_id.as_str(), + Self::Room(c) => c.relates_to.event_id.as_str(), } } @@ -495,7 +495,7 @@ impl KeyContent<'_> { pub fn flow_id(&self) -> &str { match self { Self::ToDevice(c) => &c.transaction_id, - Self::Room(c) => c.relation.event_id.as_str(), + Self::Room(c) => c.relates_to.event_id.as_str(), } } @@ -517,7 +517,7 @@ impl MacContent<'_> { pub fn flow_id(&self) -> &str { match self { Self::ToDevice(c) => &c.transaction_id, - Self::Room(c) => c.relation.event_id.as_str(), + Self::Room(c) => c.relates_to.event_id.as_str(), } } @@ -582,7 +582,7 @@ impl OwnedStartContent { pub fn flow_id(&self) -> FlowId { match self { Self::ToDevice(c) => FlowId::ToDevice(c.transaction_id.clone()), - Self::Room(r, c) => FlowId::InRoom(r.clone(), c.relation.event_id.clone()), + Self::Room(r, c) => FlowId::InRoom(r.clone(), c.relates_to.event_id.clone()), } } @@ -688,72 +688,65 @@ impl From for OutgoingContent { #[cfg(test)] impl TryFrom for OutgoingContent { - type Error = (); + type Error = String; - fn try_from(value: ToDeviceRequest) -> Result { + fn try_from(request: ToDeviceRequest) -> Result { use ruma::events::EventType; use serde_json::Value; let json: Value = serde_json::from_str( - value + request .messages .values() .next() - .and_then(|m| m.values().next().map(|j| j.get())) - .ok_or(())?, + .and_then(|m| m.values().next()) + .map(|c| c.json().get()) + .ok_or_else(|| "Content is missing from the request".to_owned())?, ) - .map_err(|_| ())?; + .map_err(|e| e.to_string())?; - match value.event_type { - EventType::KeyVerificationRequest => { - Ok(AnyToDeviceEventContent::KeyVerificationRequest( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()) - } - EventType::KeyVerificationReady => Ok(AnyToDeviceEventContent::KeyVerificationReady( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - EventType::KeyVerificationDone => Ok(AnyToDeviceEventContent::KeyVerificationDone( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - EventType::KeyVerificationStart => Ok(AnyToDeviceEventContent::KeyVerificationStart( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - EventType::KeyVerificationKey => Ok(AnyToDeviceEventContent::KeyVerificationKey( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - EventType::KeyVerificationAccept => Ok(AnyToDeviceEventContent::KeyVerificationAccept( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - EventType::KeyVerificationMac => Ok(AnyToDeviceEventContent::KeyVerificationMac( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - EventType::KeyVerificationCancel => Ok(AnyToDeviceEventContent::KeyVerificationCancel( - serde_json::from_value(json).map_err(|_| ())?, - ) - .into()), - _ => Err(()), - } + let content = match request.event_type { + EventType::KeyVerificationStart => AnyToDeviceEventContent::KeyVerificationStart( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationKey => AnyToDeviceEventContent::KeyVerificationKey( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationAccept => AnyToDeviceEventContent::KeyVerificationAccept( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationMac => AnyToDeviceEventContent::KeyVerificationMac( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationCancel => AnyToDeviceEventContent::KeyVerificationCancel( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationReady => AnyToDeviceEventContent::KeyVerificationReady( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationDone => AnyToDeviceEventContent::KeyVerificationDone( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + EventType::KeyVerificationRequest => AnyToDeviceEventContent::KeyVerificationRequest( + serde_json::from_value(json).map_err(|e| e.to_string())?, + ), + e => return Err(format!("Unsupported event type {}", e)), + }; + + Ok(content.into()) } } #[cfg(test)] impl TryFrom for OutgoingContent { - type Error = (); + type Error = String; - fn try_from(value: OutgoingRequest) -> Result { + fn try_from(value: OutgoingRequest) -> Result { match value.request() { - crate::OutgoingRequests::KeysUpload(_) => Err(()), - crate::OutgoingRequests::KeysQuery(_) => Err(()), + crate::OutgoingRequests::KeysUpload(_) => Err("Invalid request type".to_owned()), + crate::OutgoingRequests::KeysQuery(_) => Err("Invalid request type".to_owned()), crate::OutgoingRequests::ToDeviceRequest(r) => Self::try_from(r.clone()), - crate::OutgoingRequests::SignatureUpload(_) => Err(()), + crate::OutgoingRequests::SignatureUpload(_) => Err("Invalid request type".to_owned()), crate::OutgoingRequests::RoomMessage(r) => Ok(Self::from(r.clone())), } } diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 0a8f789e..fd9850e2 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -525,10 +525,9 @@ impl IdentitiesBeingVerified { #[cfg(test)] pub(crate) mod test { use ruma::{ - events::{AnyToDeviceEvent, AnyToDeviceEventContent, EventType, ToDeviceEvent}, + events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent}, UserId, }; - use serde_json::Value; use super::event_enums::OutgoingContent; use crate::{ @@ -540,7 +539,7 @@ pub(crate) mod test { sender: &UserId, request: &OutgoingVerificationRequest, ) -> AnyToDeviceEvent { - let content = get_content_from_request(request); + let content = request.to_owned().into(); wrap_any_to_device_content(sender, content) } @@ -589,36 +588,4 @@ pub(crate) mod test { _ => unreachable!(), } } - - pub(crate) fn get_content_from_request( - request: &OutgoingVerificationRequest, - ) -> OutgoingContent { - let request = - if let OutgoingVerificationRequest::ToDevice(r) = request { r } else { unreachable!() }; - - let json: Value = serde_json::from_str( - request.messages.values().next().unwrap().values().next().unwrap().get(), - ) - .unwrap(); - - match request.event_type { - 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() - } } diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 9711caa6..7eb621c6 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -19,7 +19,6 @@ use std::sync::{Arc, Mutex}; use matrix_qrcode::QrVerificationData; use matrix_sdk_common::uuid::Uuid; use ruma::{ - api::client::r0::to_device::DeviceIdOrAllDevices, events::{ key::verification::{ cancel::CancelCode, @@ -31,6 +30,7 @@ use ruma::{ room::message::KeyVerificationRequestEventContent, AnyMessageEventContent, AnyToDeviceEventContent, }, + to_device::DeviceIdOrAllDevices, DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use tracing::{info, trace, warn}; @@ -51,9 +51,9 @@ use crate::{ }; const SUPPORTED_METHODS: &[VerificationMethod] = &[ - VerificationMethod::MSasV1, - VerificationMethod::MQrCodeShowV1, - VerificationMethod::MReciprocateV1, + VerificationMethod::SasV1, + VerificationMethod::QrCodeShowV1, + VerificationMethod::ReciprocateV1, ]; /// An object controlling key verification requests. @@ -780,8 +780,8 @@ impl RequestState { // If we didn't state that we support showing QR codes or if the other // side doesn't support scanning QR codes bail early. - if !self.state.our_methods.contains(&VerificationMethod::MQrCodeShowV1) - || !self.state.their_methods.contains(&VerificationMethod::MQrScanShowV1) + if !self.state.our_methods.contains(&VerificationMethod::QrCodeShowV1) + || !self.state.their_methods.contains(&VerificationMethod::QrCodeScanV1) { return Ok(None); } @@ -942,7 +942,7 @@ impl RequestState { account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, ) -> Result, CryptoStoreError> { - if !self.state.their_methods.contains(&VerificationMethod::MSasV1) { + if !self.state.their_methods.contains(&VerificationMethod::SasV1) { return Ok(None); } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 4e50d89a..fb9a5be6 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -476,12 +476,12 @@ impl AcceptSettings { fn apply(self, mut content: OwnedAcceptContent) -> OwnedAcceptContent { match &mut content { OwnedAcceptContent::ToDevice(AcceptToDeviceEventContent { - method: AcceptMethod::MSasV1(c), + method: AcceptMethod::SasV1(c), .. }) | OwnedAcceptContent::Room( _, - AcceptEventContent { method: AcceptMethod::MSasV1(c), .. }, + AcceptEventContent { method: AcceptMethod::SasV1(c), .. }, ) => { c.short_authentication_string.retain(|sas| self.allowed_methods.contains(sas)); content diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index f3d82488..2c63bbc3 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -102,7 +102,7 @@ impl TryFrom for AcceptedProtocols { Err(CancelCode::UnknownMethod) } else { Ok(Self { - method: VerificationMethod::MSasV1, + method: VerificationMethod::SasV1, hash: content.hash, key_agreement_protocol: content.key_agreement_protocol, message_auth_code: content.message_authentication_code, @@ -149,7 +149,7 @@ impl TryFrom<&SasV1Content> for AcceptedProtocols { } Ok(Self { - method: VerificationMethod::MSasV1, + method: VerificationMethod::SasV1, hash: HashAlgorithm::Sha256, key_agreement_protocol: KeyAgreementProtocol::Curve25519HkdfSha256, message_auth_code: MessageAuthenticationCode::HkdfHmacSha256, @@ -163,7 +163,7 @@ impl TryFrom<&SasV1Content> for AcceptedProtocols { impl Default for AcceptedProtocols { fn default() -> Self { AcceptedProtocols { - method: VerificationMethod::MSasV1, + method: VerificationMethod::SasV1, hash: HashAlgorithm::Sha256, key_agreement_protocol: KeyAgreementProtocol::Curve25519HkdfSha256, message_auth_code: MessageAuthenticationCode::HkdfHmacSha256, @@ -450,7 +450,7 @@ impl SasState { ) -> Result, SasState> { self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; - if let AcceptMethod::MSasV1(content) = content.method() { + if let AcceptMethod::SasV1(content) = content.method() { let accepted_protocols = AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?; @@ -560,7 +560,7 @@ impl SasState { /// been started because of a /// m.key.verification.request -> m.key.verification.ready flow. pub fn as_content(&self) -> OwnedAcceptContent { - let method = AcceptMethod::MSasV1( + let method = AcceptMethod::SasV1( AcceptV1ContentInit { commitment: self.state.commitment.clone(), hash: self.state.accepted_protocols.hash.clone(), @@ -684,10 +684,10 @@ impl SasState { pub fn as_content(&self) -> OutgoingContent { match &*self.verification_flow_id { FlowId::ToDevice(s) => { - AnyToDeviceEventContent::KeyVerificationKey(KeyToDeviceEventContent { - transaction_id: s.to_string(), - key: self.inner.lock().unwrap().public_key(), - }) + AnyToDeviceEventContent::KeyVerificationKey(KeyToDeviceEventContent::new( + s.to_string(), + self.inner.lock().unwrap().public_key(), + )) .into() } FlowId::InRoom(r, e) => ( @@ -710,10 +710,10 @@ impl SasState { pub fn as_content(&self) -> OutgoingContent { match &*self.verification_flow_id { FlowId::ToDevice(s) => { - AnyToDeviceEventContent::KeyVerificationKey(KeyToDeviceEventContent { - transaction_id: s.to_string(), - key: self.inner.lock().unwrap().public_key(), - }) + AnyToDeviceEventContent::KeyVerificationKey(KeyToDeviceEventContent::new( + s.to_string(), + self.inner.lock().unwrap().public_key(), + )) .into() } FlowId::InRoom(r, e) => ( @@ -1090,11 +1090,12 @@ mod test { use ruma::{ events::key::verification::{ - accept::{AcceptMethod, CustomContent}, - start::{CustomContent as CustomStartContent, StartMethod}, + accept::{AcceptMethod, AcceptToDeviceEventContent}, + start::{StartMethod, StartToDeviceEventContent}, }, DeviceId, UserId, }; + use serde_json::json; use super::{Accepted, Created, SasState, Started}; use crate::{ @@ -1227,7 +1228,7 @@ mod test { let mut method = content.method_mut(); match &mut method { - AcceptMethod::MSasV1(ref mut c) => { + AcceptMethod::SasV1(ref mut c) => { c.commitment = "".to_string(); } _ => panic!("Unknown accept event content"), @@ -1266,7 +1267,7 @@ mod test { let mut method = content.method_mut(); match &mut method { - AcceptMethod::MSasV1(ref mut c) => { + AcceptMethod::SasV1(ref mut c) => { c.short_authentication_string = vec![]; } _ => panic!("Unknown accept event content"), @@ -1283,14 +1284,13 @@ mod test { async fn sas_unknown_method() { let (alice, bob) = get_sas_pair().await; - let mut content = bob.as_content(); - let method = content.method_mut(); - - *method = AcceptMethod::Custom(CustomContent { - method: "m.sas.custom".to_string(), - data: Default::default(), + let content = json!({ + "method": "m.sas.custom", + "method_data": "something", + "transaction_id": "some_id", }); + let content: AcceptToDeviceEventContent = serde_json::from_value(content).unwrap(); let content = AcceptContent::from(&content); alice @@ -1331,22 +1331,22 @@ mod test { ) .expect_err("Didn't cancel on invalid MAC method"); - let mut start_content = alice_sas.as_content(); - let method = start_content.method_mut(); - - *method = StartMethod::Custom(CustomStartContent { - method: "m.sas.custom".to_string(), - data: Default::default(), + let content = json!({ + "method": "m.sas.custom", + "from_device": "DEVICEID", + "method_data": "something", + "transaction_id": "some_id", }); - let flow_id = start_content.flow_id(); - let content = StartContent::from(&start_content); + let content: StartToDeviceEventContent = serde_json::from_value(content).unwrap(); + let content = StartContent::from(&content); + let flow_id = content.flow_id().to_owned(); SasState::::from_start_event( bob.clone(), alice_device, None, - flow_id, + flow_id.into(), &content, false, ) diff --git a/matrix_sdk_test/Cargo.toml b/matrix_sdk_test/Cargo.toml index f640d00f..cbb03617 100644 --- a/matrix_sdk_test/Cargo.toml +++ b/matrix_sdk_test/Cargo.toml @@ -18,6 +18,6 @@ http = "0.2.3" lazy_static = "1.4.0" matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } matrix-sdk-test-macros = { version = "0.1.0", path = "../matrix_sdk_test_macros" } -ruma = { version = "0.1.2", features = ["client-api-c"] } +ruma = { version = "0.1.2", features = ["client-api-c"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } serde = "1.0.122" serde_json = "1.0.61" From 58369fe7d08b20f0cd898255dad16b79665a13e8 Mon Sep 17 00:00:00 2001 From: SaurusXI Date: Mon, 21 Jun 2021 20:14:40 +0530 Subject: [PATCH 32/88] matrix-sdk: (fix) use macro for matching in are_events_visible --- matrix_sdk/src/room/common.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index dfc2cf08..f8709b66 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -184,10 +184,10 @@ impl Common { fn are_events_visible(&self) -> bool { if let RoomType::Invited = self.inner.room_type() { - return match self.inner.history_visibility() { - HistoryVisibility::WorldReadable | HistoryVisibility::Invited => true, - _ => false, - }; + return matches!( + self.inner.history_visibility(), + HistoryVisibility::WorldReadable | HistoryVisibility::Invited + ); } true From 17097f4d420160bbc35b3d181f426fdf82fe872a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 21 Jun 2021 16:42:38 +0200 Subject: [PATCH 33/88] matrix-sdk: Upgrade our deps --- matrix_qrcode/Cargo.toml | 4 +- matrix_sdk/Cargo.toml | 36 +++++++++--------- .../examples/wasm_command_bot/Cargo.toml | 8 ++-- matrix_sdk_base/Cargo.toml | 32 ++++++++-------- matrix_sdk_common/Cargo.toml | 10 ++--- matrix_sdk_crypto/Cargo.toml | 38 +++++++++---------- .../src/file_encryption/attachments.rs | 15 +++++--- .../src/file_encryption/key_export.rs | 25 ++++++++---- matrix_sdk_test/Cargo.toml | 6 +-- 9 files changed, 94 insertions(+), 80 deletions(-) diff --git a/matrix_qrcode/Cargo.toml b/matrix_qrcode/Cargo.toml index 3932e4bf..69d87fa0 100644 --- a/matrix_qrcode/Cargo.toml +++ b/matrix_qrcode/Cargo.toml @@ -19,6 +19,6 @@ base64 = "0.13.0" byteorder = "1.4.3" image = { version = "0.23.14", optional = true } qrcode = { version = "0.12.0", default-features = false } -rqrr = { version = "0.3.2" , optional = true } +rqrr = { version = "0.3.2", optional = true } ruma-identifiers = { version = "0.19.3", git = "https://github.com/ruma/ruma", rev = "d73ab8add" } -thiserror = "1.0.24" +thiserror = "1.0.25" diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index a4cec562..ef107277 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -32,15 +32,15 @@ docs = ["encryption", "sled_cryptostore", "sled_state_store", "sso_login"] [dependencies] dashmap = "4.0.2" -futures = "0.3.12" -http = "0.2.3" -serde_json = "1.0.61" -thiserror = "1.0.23" -tracing = "0.1.22" -url = "2.2.0" -zeroize = "1.2.0" +futures = "0.3.15" +http = "0.2.4" +serde_json = "1.0.64" +thiserror = "1.0.25" +tracing = "0.1.26" +url = "2.2.2" +zeroize = "1.3.0" mime = "0.3.16" -rand = { version = "0.8.2", optional = true } +rand = { version = "0.8.4", optional = true } bytes = "1.0.1" matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } @@ -51,7 +51,7 @@ path = "../matrix_sdk_base" default_features = false [dependencies.reqwest] -version = "0.11.0" +version = "0.11.3" default_features = false [dependencies.ruma] @@ -61,12 +61,12 @@ rev = "d73ab8add" features = ["client-api-c", "compat", "unstable-pre-spec"] [dependencies.tokio-stream] -version = "0.1.4" +version = "0.1.6" features = ["net"] optional = true [dependencies.warp] -version = "0.3.0" +version = "0.3.1" default-features = false optional = true @@ -75,7 +75,7 @@ version = "0.3.0" features = ["tokio"] [dependencies.tracing-futures] -version = "0.2.4" +version = "0.2.5" default-features = false features = ["std", "std-future"] @@ -83,7 +83,7 @@ features = ["std", "std-future"] futures-timer = "3.0.2" [target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio] -version = "1.1.0" +version = "1.7.1" default-features = false features = ["fs", "rt"] @@ -92,13 +92,13 @@ version = "3.0.2" features = ["wasm-bindgen"] [dev-dependencies] -dirs = "3.0.1" +dirs = "3.0.2" matrix-sdk-test = { version = "0.2.0", path = "../matrix_sdk_test" } -tokio = { version = "1.1.0", default-features = false, features = ["rt-multi-thread", "macros"] } -serde_json = "1.0.61" -tracing-subscriber = "0.2.15" +tokio = { version = "1.7.1", default-features = false, features = ["rt-multi-thread", "macros"] } +serde_json = "1.0.64" +tracing-subscriber = "0.2.18" tempfile = "3.2.0" -mockito = "0.29.0" +mockito = "0.30.0" lazy_static = "1.4.0" matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } diff --git a/matrix_sdk/examples/wasm_command_bot/Cargo.toml b/matrix_sdk/examples/wasm_command_bot/Cargo.toml index 91ad579d..0e49bf55 100644 --- a/matrix_sdk/examples/wasm_command_bot/Cargo.toml +++ b/matrix_sdk/examples/wasm_command_bot/Cargo.toml @@ -10,11 +10,11 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -url = "2.2.1" -wasm-bindgen = { version = "0.2.72", features = ["serde-serialize"] } -wasm-bindgen-futures = "0.4.22" +url = "2.2.2" +wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"] } +wasm-bindgen-futures = "0.4.24" console_error_panic_hook = "0.1.6" -web-sys = { version = "0.3.49", features = ["console"] } +web-sys = { version = "0.3.51", features = ["console"] } [dependencies.matrix-sdk] path = "../.." diff --git a/matrix_sdk_base/Cargo.toml b/matrix_sdk_base/Cargo.toml index 10af9343..9fdcd9b3 100644 --- a/matrix_sdk_base/Cargo.toml +++ b/matrix_sdk_base/Cargo.toml @@ -27,43 +27,43 @@ docs = ["encryption", "sled_cryptostore"] dashmap = "4.0.2" lru = "0.6.5" ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } -serde = { version = "1.0.122", features = ["rc"] } -serde_json = "1.0.61" -tracing = "0.1.22" +serde = { version = "1.0.126", features = ["rc"] } +serde_json = "1.0.64" +tracing = "0.1.26" matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } matrix-sdk-crypto = { version = "0.2.0", path = "../matrix_sdk_crypto", optional = true } # Misc dependencies -thiserror = "1.0.23" -futures = "0.3.12" -zeroize = { version = "1.2.0", features = ["zeroize_derive"] } +thiserror = "1.0.25" +futures = "0.3.15" +zeroize = { version = "1.3.0", features = ["zeroize_derive"] } # Deps for the sled state store sled = { version = "0.34.6", optional = true } -chacha20poly1305 = { version = "0.7.1", optional = true } -pbkdf2 = { version = "0.6.0", default-features = false, optional = true } -hmac = { version = "0.10.1", optional = true } -sha2 = { version = "0.9.2", optional = true } -rand = { version = "0.8.2", optional = true } +chacha20poly1305 = { version = "0.8.0", optional = true } +pbkdf2 = { version = "0.8.0", default-features = false, optional = true } +hmac = { version = "0.11.0", optional = true } +sha2 = { version = "0.9.5", optional = true } +rand = { version = "0.8.4", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio] -version = "1.1.0" +version = "1.7.1" default-features = false features = ["sync", "fs"] [dev-dependencies] matrix-sdk-test = { version = "0.2.0", path = "../matrix_sdk_test" } -http = "0.2.3" +http = "0.2.4" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = { version = "1.1.0", default-features = false, features = ["rt-multi-thread", "macros"] } +tokio = { version = "1.7.1", default-features = false, features = ["rt-multi-thread", "macros"] } tempfile = "3.2.0" -rustyline = "7.1.0" +rustyline = "8.2.0" rustyline-derive = "0.4.0" atty = "0.2.14" clap = "2.33.3" syntect = "4.5.0" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = "0.3.19" +wasm-bindgen-test = "0.3.24" diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index 3eb20b6d..75e5bf13 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -11,21 +11,21 @@ repository = "https://github.com/matrix-org/matrix-rust-sdk" version = "0.2.0" [dependencies] -async-trait = "0.1.42" +async-trait = "0.1.50" instant = { version = "0.1.9", features = ["wasm-bindgen", "now"] } ruma = { version = "0.1.2", features = ["client-api-c"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } -serde = "1.0.122" +serde = "1.0.126" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] uuid = { version = "0.8.2", default-features = false, features = ["v4", "serde"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio] -version = "1.1.0" +version = "1.7.1" default-features = false features = ["rt", "sync"] [target.'cfg(target_arch = "wasm32")'.dependencies] -futures = "0.3.12" +futures = "0.3.15" futures-locks = { version = "0.6.0", default-features = false } -wasm-bindgen-futures = "0.4" +wasm-bindgen-futures = "0.4.24" uuid = { version = "0.8.2", default-features = false, features = ["v4", "wasm-bindgen"] } diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index cfa3c4da..de8b3ba2 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -24,39 +24,39 @@ matrix-qrcode = { version = "0.1.0", path = "../matrix_qrcode" } matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } -olm-rs = { version = "1.0.0", features = ["serde"] } -getrandom = "0.2.2" -serde = { version = "1.0.122", features = ["derive", "rc"] } -serde_json = "1.0.61" -zeroize = { version = "1.2.0", features = ["zeroize_derive"] } +olm-rs = { version = "1.0.1", features = ["serde"] } +getrandom = "0.2.3" +serde = { version = "1.0.126", features = ["derive", "rc"] } +serde_json = "1.0.64" +zeroize = { version = "1.3.0", features = ["zeroize_derive"] } # Misc dependencies -futures = "0.3.12" +futures = "0.3.15" sled = { version = "0.34.6", optional = true } -thiserror = "1.0.23" -tracing = "0.1.22" +thiserror = "1.0.25" +tracing = "0.1.26" atomic = "0.5.0" dashmap = "4.0.2" -sha2 = "0.9.2" -aes-gcm = "0.8.0" -aes-ctr = "0.6.0" -pbkdf2 = { version = "0.6.0", default-features = false } -hmac = "0.10.1" +sha2 = "0.9.5" +aes-gcm = "0.9.2" +aes = { version = "0.7.4", features = ["ctr"] } +pbkdf2 = { version = "0.8.0", default-features = false } +hmac = "0.11.0" base64 = "0.13.0" -byteorder = "1.4.2" +byteorder = "1.4.3" [dev-dependencies] -tokio = { version = "1.1.0", default-features = false, features = ["rt-multi-thread", "macros"] } -proptest = "0.10.1" -serde_json = "1.0.61" +tokio = { version = "1.7.1", default-features = false, features = ["rt-multi-thread", "macros"] } +proptest = "1.0.0" +serde_json = "1.0.64" tempfile = "3.2.0" -http = "0.2.3" +http = "0.2.4" matrix-sdk-test = { version = "0.2.0", path = "../matrix_sdk_test" } indoc = "1.0.3" criterion = { version = "0.3.4", features = ["async", "async_tokio", "html_reports"] } [target.'cfg(target_os = "linux")'.dev-dependencies] -pprof = { version = "0.4.2", features = ["flamegraph"] } +pprof = { version = "0.4.3", features = ["flamegraph"] } [[bench]] name = "crypto_bench" diff --git a/matrix_sdk_crypto/src/file_encryption/attachments.rs b/matrix_sdk_crypto/src/file_encryption/attachments.rs index d163c780..a2dc57dc 100644 --- a/matrix_sdk_crypto/src/file_encryption/attachments.rs +++ b/matrix_sdk_crypto/src/file_encryption/attachments.rs @@ -17,9 +17,9 @@ use std::{ io::{Error as IoError, ErrorKind, Read}, }; -use aes_ctr::{ - cipher::{NewStreamCipher, SyncStreamCipher}, - Aes256Ctr, +use aes::{ + cipher::{generic_array::GenericArray, FromBlockCipher, NewBlockCipher, StreamCipher}, + Aes256, Aes256Ctr, }; use base64::DecodeError; use getrandom::getrandom; @@ -126,9 +126,11 @@ impl<'a, R: Read + 'a> AttachmentDecryptor<'a, R> { let hash = decode(info.hashes.get("sha256").ok_or(DecryptorError::MissingHash)?)?; let key = Zeroizing::from(decode_url_safe(info.web_key.k)?); let iv = decode(info.iv)?; + let iv = GenericArray::from_exact_iter(iv).ok_or(DecryptorError::KeyNonceLength)?; let sha = Sha256::default(); - let aes = Aes256Ctr::new_var(&key, &iv).map_err(|_| DecryptorError::KeyNonceLength)?; + let aes = Aes256::new_from_slice(&key).map_err(|_| DecryptorError::KeyNonceLength)?; + let aes = Aes256Ctr::from_block_cipher(aes, &iv); Ok(AttachmentDecryptor { inner_reader: input, expected_hash: hash, sha, aes }) } @@ -209,8 +211,11 @@ impl<'a, R: Read + 'a> AttachmentEncryptor<'a, R> { ext: true, }); let encoded_iv = encode(&*iv); + let iv = GenericArray::from_slice(&*iv); + let key = GenericArray::from_slice(&*key); - let aes = Aes256Ctr::new_var(&*key, &*iv).expect("Cannot create AES encryption object."); + let aes = Aes256::new(key); + let aes = Aes256Ctr::from_block_cipher(aes, iv); AttachmentEncryptor { finished: false, diff --git a/matrix_sdk_crypto/src/file_encryption/key_export.rs b/matrix_sdk_crypto/src/file_encryption/key_export.rs index 95cc8bb6..be191420 100644 --- a/matrix_sdk_crypto/src/file_encryption/key_export.rs +++ b/matrix_sdk_crypto/src/file_encryption/key_export.rs @@ -14,9 +14,9 @@ use std::io::{Cursor, Read, Seek, SeekFrom}; -use aes_ctr::{ - cipher::{NewStreamCipher, SyncStreamCipher}, - Aes256Ctr, +use aes::{ + cipher::{generic_array::GenericArray, FromBlockCipher, NewBlockCipher, StreamCipher}, + Aes256, Aes256Ctr, }; use byteorder::{BigEndian, ReadBytesExt}; use getrandom::getrandom; @@ -161,7 +161,12 @@ fn encrypt_helper(mut plaintext: &mut [u8], passphrase: &str, rounds: u32) -> St pbkdf2::>(passphrase.as_bytes(), &salt, rounds, &mut derived_keys); let (key, hmac_key) = derived_keys.split_at(KEY_SIZE); - let mut aes = Aes256Ctr::new_var(key, &iv.to_be_bytes()).expect("Can't create AES object"); + let key = GenericArray::from_slice(key); + let iv = iv.to_be_bytes(); + let iv = GenericArray::from_slice(&iv); + + let aes = Aes256::new(key); + let mut aes = Aes256Ctr::from_block_cipher(aes, iv); aes.apply_keystream(&mut plaintext); @@ -169,11 +174,11 @@ fn encrypt_helper(mut plaintext: &mut [u8], passphrase: &str, rounds: u32) -> St payload.extend(&VERSION.to_be_bytes()); payload.extend(&salt); - payload.extend(&iv.to_be_bytes()); + payload.extend(&*iv); payload.extend(&rounds.to_be_bytes()); payload.extend_from_slice(plaintext); - let mut hmac = Hmac::::new_varkey(hmac_key).expect("Can't create HMAC object"); + let mut hmac = Hmac::::new_from_slice(hmac_key).expect("Can't create HMAC object"); hmac.update(&payload); let mac = hmac.finalize(); @@ -213,12 +218,16 @@ fn decrypt_helper(ciphertext: &str, passphrase: &str) -> Result>(passphrase.as_bytes(), &salt, rounds, &mut derived_keys); let (key, hmac_key) = derived_keys.split_at(KEY_SIZE); - let mut hmac = Hmac::::new_varkey(hmac_key).expect("Can't create an HMAC object"); + let mut hmac = Hmac::::new_from_slice(hmac_key).expect("Can't create an HMAC object"); hmac.update(&decoded[0..ciphertext_end]); hmac.verify(&mac).map_err(|_| KeyExportError::InvalidMac)?; + let key = GenericArray::from_slice(key); + let iv = GenericArray::from_slice(&iv); + let mut ciphertext = &mut decoded[ciphertext_start..ciphertext_end]; - let mut aes = Aes256Ctr::new_var(key, &iv).expect("Can't create an AES object"); + let aes = Aes256::new(key); + let mut aes = Aes256Ctr::from_block_cipher(aes, iv); aes.apply_keystream(&mut ciphertext); Ok(String::from_utf8(ciphertext.to_owned())?) diff --git a/matrix_sdk_test/Cargo.toml b/matrix_sdk_test/Cargo.toml index cbb03617..5366dba3 100644 --- a/matrix_sdk_test/Cargo.toml +++ b/matrix_sdk_test/Cargo.toml @@ -14,10 +14,10 @@ version = "0.2.0" appservice = [] [dependencies] -http = "0.2.3" +http = "0.2.4" lazy_static = "1.4.0" matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } matrix-sdk-test-macros = { version = "0.1.0", path = "../matrix_sdk_test_macros" } ruma = { version = "0.1.2", features = ["client-api-c"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } -serde = "1.0.122" -serde_json = "1.0.61" +serde = "1.0.126" +serde_json = "1.0.64" From 513fbd8900306950ec60b7ad5f0018c39883121e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 21 Jun 2021 17:29:46 +0200 Subject: [PATCH 34/88] crypto: Manually implement Debug for attachment encryptors/decryptors --- .../src/file_encryption/attachments.rs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/matrix_sdk_crypto/src/file_encryption/attachments.rs b/matrix_sdk_crypto/src/file_encryption/attachments.rs index a2dc57dc..cdf82ab2 100644 --- a/matrix_sdk_crypto/src/file_encryption/attachments.rs +++ b/matrix_sdk_crypto/src/file_encryption/attachments.rs @@ -37,17 +37,25 @@ const VERSION: &str = "v2"; /// A wrapper that transparently encrypts anything that implements `Read` as an /// Matrix attachment. -#[derive(Debug)] pub struct AttachmentDecryptor<'a, R: 'a + Read> { - inner_reader: &'a mut R, + inner: &'a mut R, expected_hash: Vec, sha: Sha256, aes: Aes256Ctr, } +impl<'a, R: 'a + Read + std::fmt::Debug> std::fmt::Debug for AttachmentDecryptor<'a, R> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AttachmentDecryptor") + .field("inner", &self.inner) + .field("expected_hash", &self.expected_hash) + .finish() + } +} + impl<'a, R: Read> Read for AttachmentDecryptor<'a, R> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let read_bytes = self.inner_reader.read(buf)?; + let read_bytes = self.inner.read(buf)?; if read_bytes == 0 { let hash = self.sha.finalize_reset(); @@ -132,15 +140,14 @@ impl<'a, R: Read + 'a> AttachmentDecryptor<'a, R> { let aes = Aes256::new_from_slice(&key).map_err(|_| DecryptorError::KeyNonceLength)?; let aes = Aes256Ctr::from_block_cipher(aes, &iv); - Ok(AttachmentDecryptor { inner_reader: input, expected_hash: hash, sha, aes }) + Ok(AttachmentDecryptor { inner: input, expected_hash: hash, sha, aes }) } } /// A wrapper that transparently encrypts anything that implements `Read`. -#[derive(Debug)] pub struct AttachmentEncryptor<'a, R: Read + 'a> { finished: bool, - inner_reader: &'a mut R, + inner: &'a mut R, web_key: JsonWebKey, iv: String, hashes: BTreeMap, @@ -148,9 +155,18 @@ pub struct AttachmentEncryptor<'a, R: Read + 'a> { sha: Sha256, } +impl<'a, R: 'a + Read + std::fmt::Debug> std::fmt::Debug for AttachmentEncryptor<'a, R> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AttachmentEncryptor") + .field("inner", &self.inner) + .field("finished", &self.finished) + .finish() + } +} + impl<'a, R: Read + 'a> Read for AttachmentEncryptor<'a, R> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let read_bytes = self.inner_reader.read(buf)?; + let read_bytes = self.inner.read(buf)?; if read_bytes == 0 { let hash = self.sha.finalize_reset(); @@ -219,7 +235,7 @@ impl<'a, R: Read + 'a> AttachmentEncryptor<'a, R> { AttachmentEncryptor { finished: false, - inner_reader: reader, + inner: reader, iv: encoded_iv, web_key, hashes: BTreeMap::new(), From 57b2f6ad22f4199a6054ce70b5e32462153f7fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 21 Jun 2021 19:53:26 +0200 Subject: [PATCH 35/88] matrix-sdk: Switch to a release of ruma --- matrix_qrcode/Cargo.toml | 2 +- matrix_sdk/Cargo.toml | 4 +--- matrix_sdk_appservice/Cargo.toml | 4 +--- matrix_sdk_base/Cargo.toml | 2 +- matrix_sdk_common/Cargo.toml | 2 +- matrix_sdk_crypto/Cargo.toml | 2 +- matrix_sdk_test/Cargo.toml | 2 +- 7 files changed, 7 insertions(+), 11 deletions(-) diff --git a/matrix_qrcode/Cargo.toml b/matrix_qrcode/Cargo.toml index 69d87fa0..47a14c7f 100644 --- a/matrix_qrcode/Cargo.toml +++ b/matrix_qrcode/Cargo.toml @@ -20,5 +20,5 @@ byteorder = "1.4.3" image = { version = "0.23.14", optional = true } qrcode = { version = "0.12.0", default-features = false } rqrr = { version = "0.3.2", optional = true } -ruma-identifiers = { version = "0.19.3", git = "https://github.com/ruma/ruma", rev = "d73ab8add" } +ruma-identifiers = "0.19.3" thiserror = "1.0.25" diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index ef107277..eea9b3c7 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -55,9 +55,7 @@ version = "0.11.3" default_features = false [dependencies.ruma] -version = "0.1.2" -git = "https://github.com/ruma/ruma" -rev = "d73ab8add" +version = "0.2.0" features = ["client-api-c", "compat", "unstable-pre-spec"] [dependencies.tokio-stream] diff --git a/matrix_sdk_appservice/Cargo.toml b/matrix_sdk_appservice/Cargo.toml index 4a21b3df..6d38a561 100644 --- a/matrix_sdk_appservice/Cargo.toml +++ b/matrix_sdk_appservice/Cargo.toml @@ -32,9 +32,7 @@ warp = { git = "https://github.com/seanmonstar/warp.git", rev = "629405", option matrix-sdk = { version = "0.2", path = "../matrix_sdk", default-features = false, features = ["appservice", "native-tls"] } [dependencies.ruma] -version = "0.1.2" -git = "https://github.com/ruma/ruma" -rev = "d73ab8add" +version = "0.2.0" features = ["client-api-c", "appservice-api-s", "unstable-pre-spec"] [dev-dependencies] diff --git a/matrix_sdk_base/Cargo.toml b/matrix_sdk_base/Cargo.toml index 9fdcd9b3..063bcf23 100644 --- a/matrix_sdk_base/Cargo.toml +++ b/matrix_sdk_base/Cargo.toml @@ -26,7 +26,7 @@ docs = ["encryption", "sled_cryptostore"] [dependencies] dashmap = "4.0.2" lru = "0.6.5" -ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } +ruma = { version = "0.2.0", features = ["client-api-c", "unstable-pre-spec"] } serde = { version = "1.0.126", features = ["rc"] } serde_json = "1.0.64" tracing = "0.1.26" diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index 75e5bf13..886c8e3e 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -13,7 +13,7 @@ version = "0.2.0" [dependencies] async-trait = "0.1.50" instant = { version = "0.1.9", features = ["wasm-bindgen", "now"] } -ruma = { version = "0.1.2", features = ["client-api-c"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } +ruma = { version = "0.2.0", features = ["client-api-c"] } serde = "1.0.126" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index de8b3ba2..3cce6902 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -22,7 +22,7 @@ docs = ["sled_cryptostore"] [dependencies] matrix-qrcode = { version = "0.1.0", path = "../matrix_qrcode" } matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } -ruma = { version = "0.1.2", features = ["client-api-c", "unstable-pre-spec"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } +ruma = { version = "0.2.0", features = ["client-api-c", "unstable-pre-spec"] } olm-rs = { version = "1.0.1", features = ["serde"] } getrandom = "0.2.3" diff --git a/matrix_sdk_test/Cargo.toml b/matrix_sdk_test/Cargo.toml index 5366dba3..21eca785 100644 --- a/matrix_sdk_test/Cargo.toml +++ b/matrix_sdk_test/Cargo.toml @@ -18,6 +18,6 @@ http = "0.2.4" lazy_static = "1.4.0" matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } matrix-sdk-test-macros = { version = "0.1.0", path = "../matrix_sdk_test_macros" } -ruma = { version = "0.1.2", features = ["client-api-c"], git = "https://github.com/ruma/ruma", rev = "d73ab8add" } +ruma = { version = "0.2.0", features = ["client-api-c"] } serde = "1.0.126" serde_json = "1.0.64" From e7c7b63b6eca5105241716b18d0399466ba8af37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 22 Jun 2021 14:20:25 +0200 Subject: [PATCH 36/88] qrcode: Add a readme --- matrix_qrcode/README.md | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 matrix_qrcode/README.md diff --git a/matrix_qrcode/README.md b/matrix_qrcode/README.md new file mode 100644 index 00000000..9da0daeb --- /dev/null +++ b/matrix_qrcode/README.md @@ -0,0 +1,62 @@ +[![Build Status](https://img.shields.io/travis/matrix-org/matrix-rust-sdk.svg?style=flat-square)](https://travis-ci.org/matrix-org/matrix-rust-sdk) +[![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/master.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk) +[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) +[![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org) + +# matrix-qrcode + +**matrix-qrcode** is a crate to easily generate and parse QR codes for +interactive verification using [QR codes] in Matrix. + +[Matrix]: https://matrix.org/ +[Rust]: https://www.rust-lang.org/ +[QR codes]: https://spec.matrix.org/unstable/client-server-api/#qr-codes + +## Usage + +This is probably not the crate you are looking for, it's used internally in the +matrix-rust-sdk. + +If you still want to play with QR codes, here are a couple of helpful examples. + + +### Decode an image + +```rust +use image; +use matrix_qrcode::{QrVerificationData, DecodingError}; + +fn main() -> Result<(), DecodingError> { + let image = image::open("/path/to/my/image.png").unwrap(); + let result = QrVerificationData::from_image(image)?; + + Ok(()) +} +``` + +### Encode into a QR code + +```rust +use matrix_qrcode::{QrVerificationData, DecodingError}; +use image::Luma; + +fn main() -> Result<(), DecodingError> { + let data = b"MATRIX\ + \x02\x02\x00\x07\ + FLOW_ID\ + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ + BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\ + SHARED_SECRET"; + + let data = QrVerificationData::from_bytes(data)?; + let encoded = data.to_qr_code().unwrap(); + let image = encoded.render::>().build(); + + Ok(()) +} +``` + + +## License + +[Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) From b95ebe444eef1e90f682030768f4e668a7687a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 22 Jun 2021 14:57:46 +0200 Subject: [PATCH 37/88] matrix-sdk: Bump our versions --- matrix_qrcode/Cargo.toml | 6 ++++++ matrix_sdk/Cargo.toml | 9 ++++----- matrix_sdk_appservice/Cargo.toml | 4 ++-- matrix_sdk_base/Cargo.toml | 8 ++++---- matrix_sdk_common/Cargo.toml | 2 +- matrix_sdk_crypto/Cargo.toml | 6 +++--- matrix_sdk_test/Cargo.toml | 4 ++-- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/matrix_qrcode/Cargo.toml b/matrix_qrcode/Cargo.toml index 47a14c7f..8dc9cbd5 100644 --- a/matrix_qrcode/Cargo.toml +++ b/matrix_qrcode/Cargo.toml @@ -1,8 +1,14 @@ [package] name = "matrix-qrcode" +description = "Library to encode and decode QR codes for interactive verifications in Matrix land" version = "0.1.0" authors = ["Damir Jelić "] edition = "2018" +homepage = "https://github.com/matrix-org/matrix-rust-sdk" +keywords = ["matrix", "chat", "messaging", "ruma", "nio"] +license = "Apache-2.0" +readme = "README.md" +repository = "https://github.com/matrix-org/matrix-rust-sdk" [package.metadata.docs.rs] features = ["docs"] diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index eea9b3c7..10bdefc3 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" name = "matrix-sdk" readme = "README.md" repository = "https://github.com/matrix-org/matrix-rust-sdk" -version = "0.2.0" +version = "0.3.0" [package.metadata.docs.rs] features = ["docs"] @@ -43,10 +43,10 @@ mime = "0.3.16" rand = { version = "0.8.4", optional = true } bytes = "1.0.1" -matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } +matrix-sdk-common = { version = "0.3.0", path = "../matrix_sdk_common" } [dependencies.matrix-sdk-base] -version = "0.2.0" +version = "0.3.0" path = "../matrix_sdk_base" default_features = false @@ -91,14 +91,13 @@ features = ["wasm-bindgen"] [dev-dependencies] dirs = "3.0.2" -matrix-sdk-test = { version = "0.2.0", path = "../matrix_sdk_test" } +matrix-sdk-test = { version = "0.3.0", path = "../matrix_sdk_test" } tokio = { version = "1.7.1", default-features = false, features = ["rt-multi-thread", "macros"] } serde_json = "1.0.64" tracing-subscriber = "0.2.18" tempfile = "3.2.0" mockito = "0.30.0" lazy_static = "1.4.0" -matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } [[example]] name = "emoji_verification" diff --git a/matrix_sdk_appservice/Cargo.toml b/matrix_sdk_appservice/Cargo.toml index 6d38a561..4a14ea74 100644 --- a/matrix_sdk_appservice/Cargo.toml +++ b/matrix_sdk_appservice/Cargo.toml @@ -29,7 +29,7 @@ tracing = "0.1" url = "2" warp = { git = "https://github.com/seanmonstar/warp.git", rev = "629405", optional = true, default-features = false } -matrix-sdk = { version = "0.2", path = "../matrix_sdk", default-features = false, features = ["appservice", "native-tls"] } +matrix-sdk = { version = "0.3", path = "../matrix_sdk", default-features = false, features = ["appservice", "native-tls"] } [dependencies.ruma] version = "0.2.0" @@ -41,7 +41,7 @@ mockito = "0.30" tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros"] } tracing-subscriber = "0.2" -matrix-sdk-test = { version = "0.2", path = "../matrix_sdk_test", features = ["appservice"] } +matrix-sdk-test = { version = "0.3", path = "../matrix_sdk_test", features = ["appservice"] } [[example]] name = "appservice_autojoin" diff --git a/matrix_sdk_base/Cargo.toml b/matrix_sdk_base/Cargo.toml index 063bcf23..cd7b08fb 100644 --- a/matrix_sdk_base/Cargo.toml +++ b/matrix_sdk_base/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" name = "matrix-sdk-base" readme = "README.md" repository = "https://github.com/matrix-org/matrix-rust-sdk" -version = "0.2.0" +version = "0.3.0" [package.metadata.docs.rs] features = ["docs"] @@ -31,8 +31,8 @@ serde = { version = "1.0.126", features = ["rc"] } serde_json = "1.0.64" tracing = "0.1.26" -matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } -matrix-sdk-crypto = { version = "0.2.0", path = "../matrix_sdk_crypto", optional = true } +matrix-sdk-common = { version = "0.3.0", path = "../matrix_sdk_common" } +matrix-sdk-crypto = { version = "0.3.0", path = "../matrix_sdk_crypto", optional = true } # Misc dependencies thiserror = "1.0.25" @@ -53,7 +53,7 @@ default-features = false features = ["sync", "fs"] [dev-dependencies] -matrix-sdk-test = { version = "0.2.0", path = "../matrix_sdk_test" } +matrix-sdk-test = { version = "0.3.0", path = "../matrix_sdk_test" } http = "0.2.4" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] diff --git a/matrix_sdk_common/Cargo.toml b/matrix_sdk_common/Cargo.toml index 886c8e3e..2a003cf8 100644 --- a/matrix_sdk_common/Cargo.toml +++ b/matrix_sdk_common/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" name = "matrix-sdk-common" readme = "README.md" repository = "https://github.com/matrix-org/matrix-rust-sdk" -version = "0.2.0" +version = "0.3.0" [dependencies] async-trait = "0.1.50" diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index 3cce6902..7b42b582 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" name = "matrix-sdk-crypto" readme = "README.md" repository = "https://github.com/matrix-org/matrix-rust-sdk" -version = "0.2.0" +version = "0.3.0" [package.metadata.docs.rs] features = ["docs"] @@ -21,7 +21,7 @@ docs = ["sled_cryptostore"] [dependencies] matrix-qrcode = { version = "0.1.0", path = "../matrix_qrcode" } -matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } +matrix-sdk-common = { version = "0.3.0", path = "../matrix_sdk_common" } ruma = { version = "0.2.0", features = ["client-api-c", "unstable-pre-spec"] } olm-rs = { version = "1.0.1", features = ["serde"] } @@ -51,7 +51,7 @@ proptest = "1.0.0" serde_json = "1.0.64" tempfile = "3.2.0" http = "0.2.4" -matrix-sdk-test = { version = "0.2.0", path = "../matrix_sdk_test" } +matrix-sdk-test = { version = "0.3.0", path = "../matrix_sdk_test" } indoc = "1.0.3" criterion = { version = "0.3.4", features = ["async", "async_tokio", "html_reports"] } diff --git a/matrix_sdk_test/Cargo.toml b/matrix_sdk_test/Cargo.toml index 21eca785..6eaa9cc4 100644 --- a/matrix_sdk_test/Cargo.toml +++ b/matrix_sdk_test/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" name = "matrix-sdk-test" readme = "README.md" repository = "https://github.com/matrix-org/matrix-rust-sdk" -version = "0.2.0" +version = "0.3.0" [features] appservice = [] @@ -16,7 +16,7 @@ appservice = [] [dependencies] http = "0.2.4" lazy_static = "1.4.0" -matrix-sdk-common = { version = "0.2.0", path = "../matrix_sdk_common" } +matrix-sdk-common = { version = "0.3.0", path = "../matrix_sdk_common" } matrix-sdk-test-macros = { version = "0.1.0", path = "../matrix_sdk_test_macros" } ruma = { version = "0.2.0", features = ["client-api-c"] } serde = "1.0.126" From c4e4830f324ff1c0f01b6a592fda64a193e3679c Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 11:52:19 +0200 Subject: [PATCH 38/88] Add missing doc(cfg) attrs --- matrix_sdk/src/error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/matrix_sdk/src/error.rs b/matrix_sdk/src/error.rs index ca9c77d8..69d8e072 100644 --- a/matrix_sdk/src/error.rs +++ b/matrix_sdk/src/error.rs @@ -120,11 +120,13 @@ pub enum Error { /// An error occurred in the crypto store. #[cfg(feature = "encryption")] + #[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[error(transparent)] CryptoStoreError(#[from] CryptoStoreError), /// An error occurred during decryption. #[cfg(feature = "encryption")] + #[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[error(transparent)] DecryptorError(#[from] DecryptorError), From b181125e6f9cdf09b0c9f0a41830d8c9fc5bb34f Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 11:56:59 +0200 Subject: [PATCH 39/88] Remove nesting of matrix_sdk_base::Error in matrix_sdk::Error There is no useful distinction between the same error variants on matrix_sdk::Error directly vs matrix_sdk::MatrixError. --- matrix_sdk/src/error.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/matrix_sdk/src/error.rs b/matrix_sdk/src/error.rs index 69d8e072..968a9609 100644 --- a/matrix_sdk/src/error.rs +++ b/matrix_sdk/src/error.rs @@ -18,8 +18,8 @@ use std::io::Error as IoError; use http::StatusCode; #[cfg(feature = "encryption")] -use matrix_sdk_base::crypto::{store::CryptoStoreError, DecryptorError}; -use matrix_sdk_base::{Error as MatrixError, StoreError}; +use matrix_sdk_base::crypto::{CryptoStoreError, DecryptorError, MegolmError, OlmError}; +use matrix_sdk_base::{Error as SdkBaseError, StoreError}; use reqwest::Error as ReqwestError; use ruma::{ api::{ @@ -114,16 +114,24 @@ pub enum Error { #[error(transparent)] Io(#[from] IoError), - /// An error occurred in the Matrix client library. - #[error(transparent)] - MatrixError(#[from] MatrixError), - /// An error occurred in the crypto store. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[error(transparent)] CryptoStoreError(#[from] CryptoStoreError), + /// An error occurred during a E2EE operation. + #[cfg(feature = "encryption")] + #[cfg_attr(feature = "docs", doc(cfg(encryption)))] + #[error(transparent)] + OlmError(#[from] OlmError), + + /// An error occurred during a E2EE group operation. + #[cfg(feature = "encryption")] + #[cfg_attr(feature = "docs", doc(cfg(encryption)))] + #[error(transparent)] + MegolmError(#[from] MegolmError), + /// An error occurred during decryption. #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] @@ -167,6 +175,23 @@ impl Error { } } +impl From for Error { + fn from(e: SdkBaseError) -> Self { + match e { + SdkBaseError::AuthenticationRequired => Self::AuthenticationRequired, + SdkBaseError::StateStore(e) => Self::StateStore(e), + SdkBaseError::SerdeJson(e) => Self::SerdeJson(e), + SdkBaseError::IoError(e) => Self::Io(e), + #[cfg(feature = "encryption")] + SdkBaseError::CryptoStore(e) => Self::CryptoStoreError(e), + #[cfg(feature = "encryption")] + SdkBaseError::OlmError(e) => Self::OlmError(e), + #[cfg(feature = "encryption")] + SdkBaseError::MegolmError(e) => Self::MegolmError(e), + } + } +} + impl From for Error { fn from(e: ReqwestError) -> Self { Error::Http(HttpError::Reqwest(e)) From 4a7010bab15be6b5d61f6c5da7413c9918104432 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 12:06:28 +0200 Subject: [PATCH 40/88] Reexport independent external crates entirely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … instead of reexporting individual items. --- matrix_sdk/examples/autojoin.rs | 4 +- matrix_sdk/examples/command_bot.rs | 6 +- .../examples/cross_signing_bootstrap.rs | 3 +- matrix_sdk/examples/emoji_verification.rs | 8 ++- matrix_sdk/examples/get_profiles.rs | 4 +- matrix_sdk/examples/image_bot.rs | 4 +- matrix_sdk/examples/login.rs | 4 +- .../examples/wasm_command_bot/src/lib.rs | 10 +-- matrix_sdk/src/client.rs | 66 +++++++++++-------- matrix_sdk/src/device.rs | 2 +- matrix_sdk/src/event_handler/mod.rs | 2 +- matrix_sdk/src/http_client.rs | 5 +- matrix_sdk/src/lib.rs | 21 +----- matrix_sdk/src/room/common.rs | 10 +-- matrix_sdk/src/room/joined.rs | 21 +++--- matrix_sdk/src/room_member.rs | 2 +- matrix_sdk/src/verification/sas.rs | 2 +- .../examples/appservice_autojoin.rs | 12 ++-- matrix_sdk_appservice/src/lib.rs | 4 +- matrix_sdk_appservice/src/webserver/warp.rs | 8 +-- matrix_sdk_appservice/tests/tests.rs | 6 +- 21 files changed, 105 insertions(+), 99 deletions(-) diff --git a/matrix_sdk/examples/autojoin.rs b/matrix_sdk/examples/autojoin.rs index f2ac3c1b..6838b7c1 100644 --- a/matrix_sdk/examples/autojoin.rs +++ b/matrix_sdk/examples/autojoin.rs @@ -1,9 +1,9 @@ use std::{env, process::exit}; use matrix_sdk::{ - self, async_trait, - events::{room::member::MemberEventContent, StrippedStateEvent}, + async_trait, room::Room, + ruma::events::{room::member::MemberEventContent, StrippedStateEvent}, Client, ClientConfig, EventHandler, SyncSettings, }; use tokio::time::{sleep, Duration}; diff --git a/matrix_sdk/examples/command_bot.rs b/matrix_sdk/examples/command_bot.rs index d6c00fda..90f00e38 100644 --- a/matrix_sdk/examples/command_bot.rs +++ b/matrix_sdk/examples/command_bot.rs @@ -1,12 +1,12 @@ use std::{env, process::exit}; use matrix_sdk::{ - self, async_trait, - events::{ + async_trait, + room::Room, + ruma::events::{ room::message::{MessageEventContent, MessageType, TextMessageEventContent}, AnyMessageEventContent, SyncMessageEvent, }, - room::Room, Client, ClientConfig, EventHandler, SyncSettings, }; use url::Url; diff --git a/matrix_sdk/examples/cross_signing_bootstrap.rs b/matrix_sdk/examples/cross_signing_bootstrap.rs index 107cfc58..cb6a5c74 100644 --- a/matrix_sdk/examples/cross_signing_bootstrap.rs +++ b/matrix_sdk/examples/cross_signing_bootstrap.rs @@ -6,7 +6,8 @@ use std::{ }; use matrix_sdk::{ - self, api::r0::uiaa::AuthData, identifiers::UserId, Client, LoopCtrl, SyncSettings, + ruma::{api::client::r0::uiaa::AuthData, UserId}, + Client, LoopCtrl, SyncSettings, }; use serde_json::json; use url::Url; diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 60e93b6c..90c7fd43 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -9,8 +9,12 @@ use std::{ use matrix_sdk::{ self, - events::{room::message::MessageType, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent}, - identifiers::UserId, + ruma::{ + events::{ + room::message::MessageType, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent, + }, + UserId, + }, verification::{SasVerification, Verification}, Client, LoopCtrl, SyncSettings, }; diff --git a/matrix_sdk/examples/get_profiles.rs b/matrix_sdk/examples/get_profiles.rs index 1e8f5719..edbfcb1c 100644 --- a/matrix_sdk/examples/get_profiles.rs +++ b/matrix_sdk/examples/get_profiles.rs @@ -1,9 +1,7 @@ use std::{convert::TryFrom, env, process::exit}; use matrix_sdk::{ - self, - api::r0::profile, - identifiers::{MxcUri, UserId}, + ruma::{api::client::r0::profile, MxcUri, UserId}, Client, Result as MatrixResult, }; use url::Url; diff --git a/matrix_sdk/examples/image_bot.rs b/matrix_sdk/examples/image_bot.rs index 4408e1c3..7df95ffd 100644 --- a/matrix_sdk/examples/image_bot.rs +++ b/matrix_sdk/examples/image_bot.rs @@ -9,11 +9,11 @@ use std::{ use matrix_sdk::{ self, async_trait, - events::{ + room::Room, + ruma::events::{ room::message::{MessageEventContent, MessageType, TextMessageEventContent}, SyncMessageEvent, }, - room::Room, Client, EventHandler, SyncSettings, }; use tokio::sync::Mutex; diff --git a/matrix_sdk/examples/login.rs b/matrix_sdk/examples/login.rs index 31a71acd..fa6ab054 100644 --- a/matrix_sdk/examples/login.rs +++ b/matrix_sdk/examples/login.rs @@ -2,11 +2,11 @@ use std::{env, process::exit}; use matrix_sdk::{ self, async_trait, - events::{ + room::Room, + ruma::events::{ room::message::{MessageEventContent, MessageType, TextMessageEventContent}, SyncMessageEvent, }, - room::Room, Client, EventHandler, SyncSettings, }; use url::Url; diff --git a/matrix_sdk/examples/wasm_command_bot/src/lib.rs b/matrix_sdk/examples/wasm_command_bot/src/lib.rs index 7f4abbb4..a94cafab 100644 --- a/matrix_sdk/examples/wasm_command_bot/src/lib.rs +++ b/matrix_sdk/examples/wasm_command_bot/src/lib.rs @@ -1,10 +1,12 @@ use matrix_sdk::{ deserialized_responses::SyncResponse, - events::{ - room::message::{MessageEventContent, MessageType, TextMessageEventContent}, - AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, SyncMessageEvent, + ruma::{ + events::{ + room::message::{MessageEventContent, MessageType, TextMessageEventContent}, + AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, SyncMessageEvent, + }, + RoomId, }, - identifiers::RoomId, Client, LoopCtrl, SyncSettings, }; use url::Url; diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 21de669f..74b61ef5 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -571,7 +571,7 @@ impl Client { /// # Example /// ```no_run /// # use std::convert::TryFrom; - /// # use matrix_sdk::{Client, identifiers::UserId}; + /// # use matrix_sdk::{Client, ruma::UserId}; /// # use futures::executor::block_on; /// let alice = UserId::try_from("@alice:example.org").unwrap(); /// # block_on(async { @@ -792,7 +792,7 @@ impl Client { /// ```no_run /// # use futures::executor::block_on; /// # use matrix_sdk::Client; - /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::ruma::room_id; /// # use matrix_sdk::media::MediaFormat; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -1001,8 +1001,7 @@ impl Client { /// ```no_run /// # use std::convert::TryFrom; /// # use matrix_sdk::Client; - /// # use matrix_sdk::identifiers::DeviceId; - /// # use matrix_sdk::assign; + /// # use matrix_sdk::ruma::{assign, DeviceId}; /// # use futures::executor::block_on; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -1260,8 +1259,7 @@ impl Client { /// ```no_run /// # use std::convert::TryFrom; /// # use matrix_sdk::Client; - /// # use matrix_sdk::identifiers::DeviceId; - /// # use matrix_sdk::assign; + /// # use matrix_sdk::ruma::{assign, DeviceId}; /// # use futures::executor::block_on; /// # use url::Url; /// # let homeserver = Url::parse("https://example.com").unwrap(); @@ -1340,10 +1338,13 @@ impl Client { /// ```no_run /// # use std::convert::TryFrom; /// # use matrix_sdk::Client; - /// # use matrix_sdk::api::r0::account::register::{Request as RegistrationRequest, RegistrationKind}; - /// # use matrix_sdk::api::r0::uiaa::AuthData; - /// # use matrix_sdk::identifiers::DeviceId; - /// # use matrix_sdk::assign; + /// # use matrix_sdk::ruma::{ + /// # api::client::r0::{ + /// # account::register::{Request as RegistrationRequest, RegistrationKind}, + /// # uiaa::AuthData, + /// # }, + /// # assign, DeviceId, + /// # }; /// # use futures::executor::block_on; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -1393,7 +1394,7 @@ impl Client { /// ```no_run /// # use matrix_sdk::{ /// # Client, SyncSettings, - /// # api::r0::{ + /// # ruma::api::client::r0::{ /// # filter::{ /// # FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter, /// # }, @@ -1537,7 +1538,10 @@ impl Client { /// # Examples /// ```no_run /// use matrix_sdk::Client; - /// # use matrix_sdk::api::r0::room::{create_room::Request as CreateRoomRequest, Visibility}; + /// # use matrix_sdk::ruma::api::client::r0::room::{ + /// # create_room::Request as CreateRoomRequest, + /// # Visibility, + /// # }; /// # use url::Url; /// /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -1570,9 +1574,11 @@ impl Client { /// ```no_run /// # use std::convert::TryFrom; /// # use matrix_sdk::Client; - /// # use matrix_sdk::directory::{Filter, RoomNetwork}; - /// # use matrix_sdk::api::r0::directory::get_public_rooms_filtered::Request as PublicRoomsFilterRequest; - /// # use matrix_sdk::assign; + /// # use matrix_sdk::ruma::{ + /// # api::client::r0::directory::get_public_rooms_filtered::Request as PublicRoomsFilterRequest, + /// # directory::{Filter, RoomNetwork}, + /// # assign, + /// # }; /// # use url::Url; /// # use futures::executor::block_on; /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -1623,7 +1629,7 @@ impl Client { /// /// ```no_run /// # use std::{path::PathBuf, fs::File, io::Read}; - /// # use matrix_sdk::{Client, identifiers::room_id}; + /// # use matrix_sdk::{Client, ruma::room_id}; /// # use url::Url; /// # use futures::executor::block_on; /// # use mime; @@ -1690,9 +1696,9 @@ impl Client { /// # use matrix_sdk::{Client, SyncSettings}; /// # use url::Url; /// # use futures::executor::block_on; - /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::ruma::room_id; /// # use std::convert::TryFrom; - /// use matrix_sdk::events::{ + /// use matrix_sdk::ruma::events::{ /// AnyMessageEventContent, /// room::message::{MessageEventContent, TextMessageEventContent}, /// }; @@ -1752,8 +1758,7 @@ impl Client { /// # block_on(async { /// # let homeserver = Url::parse("http://localhost:8080").unwrap(); /// # let mut client = Client::new(homeserver).unwrap(); - /// use matrix_sdk::api::r0::profile; - /// use matrix_sdk::identifiers::user_id; + /// use matrix_sdk::ruma::{api::client::r0::profile, user_id}; /// /// // First construct the request you want to make /// // See https://docs.rs/ruma-client-api/latest/ruma_client_api/index.html @@ -1839,8 +1844,11 @@ impl Client { /// /// ```no_run /// # use matrix_sdk::{ - /// # api::r0::uiaa::{UiaaResponse, AuthData}, - /// # Client, SyncSettings, Error, FromHttpResponseError, ServerError, + /// # ruma::api::{ + /// # client::r0::uiaa::{UiaaResponse, AuthData}, + /// # error::{FromHttpResponseError, ServerError}, + /// # }, + /// # Client, Error, SyncSettings, /// # }; /// # use futures::executor::block_on; /// # use serde_json::json; @@ -1959,7 +1967,7 @@ impl Client { /// UI thread. /// /// ```no_run - /// # use matrix_sdk::events::{ + /// # use matrix_sdk::ruma::events::{ /// # room::message::{MessageEvent, MessageEventContent, TextMessageEventContent}, /// # }; /// # use std::sync::{Arc, RwLock}; @@ -2228,7 +2236,7 @@ impl Client { /// /// ```no_run /// # use std::convert::TryFrom; - /// # use matrix_sdk::{Client, identifiers::UserId}; + /// # use matrix_sdk::{Client, ruma::UserId}; /// # use url::Url; /// # use futures::executor::block_on; /// # let alice = UserId::try_from("@alice:example.org").unwrap(); @@ -2270,8 +2278,8 @@ impl Client { /// # Examples /// ```no_run /// # use std::{convert::TryFrom, collections::BTreeMap}; - /// # use matrix_sdk::{Client, identifiers::UserId}; - /// # use matrix_sdk::api::r0::uiaa::AuthData; + /// # use matrix_sdk::{Client, ruma::UserId}; + /// # use matrix_sdk::ruma::api::client::r0::uiaa::AuthData; /// # use url::Url; /// # use futures::executor::block_on; /// # use serde_json::json; @@ -2341,7 +2349,7 @@ impl Client { /// /// ```no_run /// # use std::convert::TryFrom; - /// # use matrix_sdk::{Client, identifiers::UserId}; + /// # use matrix_sdk::{Client, ruma::UserId}; /// # use url::Url; /// # use futures::executor::block_on; /// # let alice = UserId::try_from("@alice:example.org").unwrap(); @@ -2395,7 +2403,7 @@ impl Client { /// # use std::{path::PathBuf, time::Duration}; /// # use matrix_sdk::{ /// # Client, SyncSettings, - /// # identifiers::room_id, + /// # ruma::room_id, /// # }; /// # use futures::executor::block_on; /// # use url::Url; @@ -2465,7 +2473,7 @@ impl Client { /// # use std::{path::PathBuf, time::Duration}; /// # use matrix_sdk::{ /// # Client, SyncSettings, - /// # identifiers::room_id, + /// # ruma::room_id, /// # }; /// # use futures::executor::block_on; /// # use url::Url; diff --git a/matrix_sdk/src/device.rs b/matrix_sdk/src/device.rs index 275060a9..c9e329c6 100644 --- a/matrix_sdk/src/device.rs +++ b/matrix_sdk/src/device.rs @@ -47,7 +47,7 @@ impl Device { /// /// ```no_run /// # use std::convert::TryFrom; - /// # use matrix_sdk::{Client, identifiers::UserId}; + /// # use matrix_sdk::{Client, ruma::UserId}; /// # use url::Url; /// # use futures::executor::block_on; /// # let alice = UserId::try_from("@alice:example.org").unwrap(); diff --git a/matrix_sdk/src/event_handler/mod.rs b/matrix_sdk/src/event_handler/mod.rs index 666bb757..5b21b0a7 100644 --- a/matrix_sdk/src/event_handler/mod.rs +++ b/matrix_sdk/src/event_handler/mod.rs @@ -375,7 +375,7 @@ pub enum CustomEvent<'c> { /// # use matrix_sdk::{ /// # async_trait, /// # EventHandler, -/// # events::{ +/// # ruma::events::{ /// # room::message::{MessageEventContent, MessageType, TextMessageEventContent}, /// # SyncMessageEvent /// # }, diff --git a/matrix_sdk/src/http_client.rs b/matrix_sdk/src/http_client.rs index 1899b5af..6874b90f 100644 --- a/matrix_sdk/src/http_client.rs +++ b/matrix_sdk/src/http_client.rs @@ -18,6 +18,7 @@ use std::{convert::TryFrom, fmt::Debug, sync::Arc}; #[cfg(all(not(target_arch = "wasm32")))] use backoff::{future::retry, Error as RetryError, ExponentialBackoff}; +use bytes::{Bytes, BytesMut}; #[cfg(all(not(target_arch = "wasm32")))] use http::StatusCode; use http::{HeaderValue, Response as HttpResponse}; @@ -30,7 +31,7 @@ use ruma::api::{ use tracing::trace; use url::Url; -use crate::{error::HttpError, Bytes, BytesMut, ClientConfig, RequestConfig, Session}; +use crate::{error::HttpError, ClientConfig, RequestConfig, Session}; /// Abstraction around the http layer. The allows implementors to use different /// http libraries. @@ -54,7 +55,7 @@ pub trait HttpSend: AsyncTraitDeps { /// /// ``` /// use std::convert::TryFrom; - /// use matrix_sdk::{HttpSend, async_trait, HttpError, RequestConfig, Bytes}; + /// use matrix_sdk::{HttpSend, async_trait, HttpError, RequestConfig, bytes::Bytes}; /// /// #[derive(Debug)] /// struct Client(reqwest::Client); diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index eb28e8bd..2397880a 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -75,7 +75,7 @@ compile_error!("only one of 'native-tls' or 'rustls-tls' features can be enabled #[cfg(all(feature = "sso_login", target_arch = "wasm32"))] compile_error!("'sso_login' cannot be enabled on 'wasm32' arch"); -pub use bytes::{Bytes, BytesMut}; +pub use bytes; #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub use matrix_sdk_base::crypto::{EncryptionInfo, LocalTrust}; @@ -85,23 +85,8 @@ pub use matrix_sdk_base::{ }; pub use matrix_sdk_common::*; pub use reqwest; -#[cfg(feature = "appservice")] -pub use ruma::{ - api::{appservice as api_appservice, IncomingRequest, OutgoingRequestAppserviceExt}, - serde::{exports::serde::de::value::Error as SerdeError, urlencoded}, -}; -pub use ruma::{ - api::{ - client as api, - error::{ - FromHttpRequestError, FromHttpResponseError, IntoHttpError, MatrixError, ServerError, - }, - AuthScheme, EndpointError, IncomingResponse, OutgoingRequest, SendAccessToken, - }, - assign, directory, encryption, events, identifiers, int, presence, push, receipt, - serde::{CanonicalJsonValue, Raw}, - thirdparty, uint, Int, MilliSecondsSinceUnixEpoch, Outgoing, SecondsSinceUnixEpoch, UInt, -}; +#[doc(no_inline)] +pub use ruma; mod client; mod error; diff --git a/matrix_sdk/src/room/common.rs b/matrix_sdk/src/room/common.rs index f8709b66..2b29f1bc 100644 --- a/matrix_sdk/src/room/common.rs +++ b/matrix_sdk/src/room/common.rs @@ -79,7 +79,7 @@ impl Common { /// ```no_run /// # use futures::executor::block_on; /// # use matrix_sdk::Client; - /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::ruma::room_id; /// # use matrix_sdk::media::MediaFormat; /// # use url::Url; /// # let homeserver = Url::parse("http://example.com").unwrap(); @@ -118,9 +118,11 @@ impl Common { /// ```no_run /// # use std::convert::TryFrom; /// use matrix_sdk::Client; - /// # use matrix_sdk::identifiers::room_id; - /// # use matrix_sdk::api::r0::filter::RoomEventFilter; - /// # use matrix_sdk::api::r0::message::get_message_events::Request as MessagesRequest; + /// # use matrix_sdk::ruma::room_id; + /// # use matrix_sdk::ruma::api::client::r0::{ + /// # filter::RoomEventFilter, + /// # message::get_message_events::Request as MessagesRequest, + /// # }; /// # use url::Url; /// /// # let homeserver = Url::parse("http://example.com").unwrap(); diff --git a/matrix_sdk/src/room/joined.rs b/matrix_sdk/src/room/joined.rs index 3010dd5f..e6fad924 100644 --- a/matrix_sdk/src/room/joined.rs +++ b/matrix_sdk/src/room/joined.rs @@ -159,10 +159,10 @@ impl Joined { /// /// ```no_run /// use std::time::Duration; - /// use matrix_sdk::api::r0::typing::create_typing_event::Typing; + /// use matrix_sdk::ruma::api::client::r0::typing::create_typing_event::Typing; /// # use matrix_sdk::{ /// # Client, SyncSettings, - /// # identifiers::room_id, + /// # ruma::room_id, /// # }; /// # use futures::executor::block_on; /// # use url::Url; @@ -349,9 +349,9 @@ impl Joined { /// # use matrix_sdk::{Client, SyncSettings}; /// # use url::Url; /// # use futures::executor::block_on; - /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::ruma::room_id; /// # use std::convert::TryFrom; - /// use matrix_sdk::events::{ + /// use matrix_sdk::ruma::events::{ /// AnyMessageEventContent, /// room::message::{MessageEventContent, TextMessageEventContent}, /// }; @@ -431,7 +431,7 @@ impl Joined { /// /// ```no_run /// # use std::{path::PathBuf, fs::File, io::Read}; - /// # use matrix_sdk::{Client, identifiers::room_id}; + /// # use matrix_sdk::{Client, ruma::room_id}; /// # use url::Url; /// # use mime; /// # use futures::executor::block_on; @@ -532,18 +532,17 @@ impl Joined { /// # Example /// /// ```no_run - /// use matrix_sdk::{ + /// use matrix_sdk::ruma::{ /// events::{ /// AnyStateEventContent, /// room::member::{MemberEventContent, MembershipState}, /// }, - /// identifiers::mxc_uri, - /// assign, + /// assign, mxc_uri, /// }; /// # futures::executor::block_on(async { /// # let homeserver = url::Url::parse("http://localhost:8080").unwrap(); /// # let mut client = matrix_sdk::Client::new(homeserver).unwrap(); - /// # let room_id = matrix_sdk::identifiers::room_id!("!test:localhost"); + /// # let room_id = matrix_sdk::ruma::room_id!("!test:localhost"); /// /// let avatar_url = mxc_uri!("mxc://example.org/avatar"); /// let member_event = assign!(MemberEventContent::new(MembershipState::Join), { @@ -591,11 +590,11 @@ impl Joined { /// # futures::executor::block_on(async { /// # let homeserver = url::Url::parse("http://localhost:8080").unwrap(); /// # let mut client = matrix_sdk::Client::new(homeserver).unwrap(); - /// # let room_id = matrix_sdk::identifiers::room_id!("!test:localhost"); + /// # let room_id = matrix_sdk::ruma::room_id!("!test:localhost"); /// # let room = client /// # .get_joined_room(&room_id) /// # .unwrap(); - /// let event_id = matrix_sdk::identifiers::event_id!("$xxxxxx:example.org"); + /// let event_id = matrix_sdk::ruma::event_id!("$xxxxxx:example.org"); /// let reason = Some("Indecent material"); /// room.redact(&event_id, reason, None).await.unwrap(); /// # }) diff --git a/matrix_sdk/src/room_member.rs b/matrix_sdk/src/room_member.rs index 4a6a5fa1..488ffee8 100644 --- a/matrix_sdk/src/room_member.rs +++ b/matrix_sdk/src/room_member.rs @@ -39,7 +39,7 @@ impl RoomMember { /// ```no_run /// # use futures::executor::block_on; /// # use matrix_sdk::Client; - /// # use matrix_sdk::identifiers::room_id; + /// # use matrix_sdk::ruma::room_id; /// # use matrix_sdk::RoomMember; /// # use matrix_sdk::media::MediaFormat; /// # use url::Url; diff --git a/matrix_sdk/src/verification/sas.rs b/matrix_sdk/src/verification/sas.rs index 124b6504..c2e90dfe 100644 --- a/matrix_sdk/src/verification/sas.rs +++ b/matrix_sdk/src/verification/sas.rs @@ -45,7 +45,7 @@ impl SasVerification { /// # use ruma::identifiers::user_id; /// use matrix_sdk::verification::SasVerification; /// use matrix_sdk_base::crypto::AcceptSettings; - /// use matrix_sdk::events::key::verification::ShortAuthenticationString; + /// use matrix_sdk::ruma::events::key::verification::ShortAuthenticationString; /// # let homeserver = Url::parse("http://example.com").unwrap(); /// # let client = Client::new(homeserver).unwrap(); /// # let flow_id = "someID"; diff --git a/matrix_sdk_appservice/examples/appservice_autojoin.rs b/matrix_sdk_appservice/examples/appservice_autojoin.rs index fddba03c..f792f242 100644 --- a/matrix_sdk_appservice/examples/appservice_autojoin.rs +++ b/matrix_sdk_appservice/examples/appservice_autojoin.rs @@ -3,12 +3,14 @@ use std::{convert::TryFrom, env}; use matrix_sdk_appservice::{ matrix_sdk::{ async_trait, - events::{ - room::member::{MemberEventContent, MembershipState}, - SyncStateEvent, - }, - identifiers::UserId, room::Room, + ruma::{ + events::{ + room::member::{MemberEventContent, MembershipState}, + SyncStateEvent, + }, + UserId, + }, EventHandler, }, AppService, AppServiceRegistration, diff --git a/matrix_sdk_appservice/src/lib.rs b/matrix_sdk_appservice/src/lib.rs index 6a7f6347..b4da1e55 100644 --- a/matrix_sdk_appservice/src/lib.rs +++ b/matrix_sdk_appservice/src/lib.rs @@ -89,7 +89,9 @@ use dashmap::DashMap; pub use error::Error; use http::{uri::PathAndQuery, Uri}; pub use matrix_sdk; -use matrix_sdk::{reqwest::Url, Bytes, Client, ClientConfig, EventHandler, HttpError, Session}; +use matrix_sdk::{ + bytes::Bytes, reqwest::Url, Client, ClientConfig, EventHandler, HttpError, Session, +}; use regex::Regex; #[doc(inline)] pub use ruma::api::{appservice as api, appservice::Registration}; diff --git a/matrix_sdk_appservice/src/webserver/warp.rs b/matrix_sdk_appservice/src/webserver/warp.rs index 7c584436..8e105252 100644 --- a/matrix_sdk_appservice/src/webserver/warp.rs +++ b/matrix_sdk_appservice/src/webserver/warp.rs @@ -15,7 +15,7 @@ use std::{net::ToSocketAddrs, result::Result as StdResult}; use futures::TryFutureExt; -use matrix_sdk::Bytes; +use matrix_sdk::{bytes::Bytes, ruma}; use serde::Serialize; use warp::{filters::BoxedFilter, path::FullPath, Filter, Rejection, Reply}; @@ -110,7 +110,7 @@ mod filters { .and(warp::query::raw()) .and_then(|token: String, query: String| async move { let query: Vec<(String, String)> = - matrix_sdk::urlencoded::from_str(&query).map_err(Error::from)?; + ruma::serde::urlencoded::from_str(&query).map_err(Error::from)?; if query.into_iter().any(|(key, value)| key == "access_token" && value == token) { Ok::<(), Rejection>(()) @@ -175,8 +175,8 @@ mod handlers { appservice: AppService, request: http::Request, ) -> StdResult { - let incoming_transaction: matrix_sdk::api_appservice::event::push_events::v1::IncomingRequest = - matrix_sdk::IncomingRequest::try_from_http_request(request).map_err(Error::from)?; + let incoming_transaction: ruma::api::appservice::event::push_events::v1::IncomingRequest = + ruma::api::IncomingRequest::try_from_http_request(request).map_err(Error::from)?; let client = appservice.get_cached_client(None)?; client.receive_transaction(incoming_transaction).map_err(Error::from).await?; diff --git a/matrix_sdk_appservice/tests/tests.rs b/matrix_sdk_appservice/tests/tests.rs index c68e5ad0..23dbd221 100644 --- a/matrix_sdk_appservice/tests/tests.rs +++ b/matrix_sdk_appservice/tests/tests.rs @@ -3,10 +3,12 @@ use std::sync::{Arc, Mutex}; #[cfg(feature = "actix")] use actix_web::{test as actix_test, App as ActixApp, HttpResponse}; use matrix_sdk::{ - api_appservice::Registration, async_trait, - events::{room::member::MemberEventContent, SyncStateEvent}, room::Room, + ruma::{ + api::appservice::Registration, + events::{room::member::MemberEventContent, SyncStateEvent}, + }, ClientConfig, EventHandler, RequestConfig, }; use matrix_sdk_appservice::*; From 6e29251886171e86979cdfe8506e12c622688b91 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 12:21:51 +0200 Subject: [PATCH 41/88] Remove matrix_sdk_base::Error re-export It's not part of the public API. --- matrix_sdk/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index 2397880a..be0c099e 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -80,8 +80,8 @@ pub use bytes; #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub use matrix_sdk_base::crypto::{EncryptionInfo, LocalTrust}; pub use matrix_sdk_base::{ - media, Error as BaseError, Room as BaseRoom, RoomInfo, RoomMember as BaseRoomMember, RoomType, - Session, StateChanges, StoreError, + media, Room as BaseRoom, RoomInfo, RoomMember as BaseRoomMember, RoomType, Session, + StateChanges, StoreError, }; pub use matrix_sdk_common::*; pub use reqwest; From 089ab20e7c42bc8982f14e900a68a5d06e048add Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 14:04:10 +0200 Subject: [PATCH 42/88] Re-format wasm_command_bot --- matrix_sdk/examples/wasm_command_bot/src/lib.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/matrix_sdk/examples/wasm_command_bot/src/lib.rs b/matrix_sdk/examples/wasm_command_bot/src/lib.rs index a94cafab..ddc6744b 100644 --- a/matrix_sdk/examples/wasm_command_bot/src/lib.rs +++ b/matrix_sdk/examples/wasm_command_bot/src/lib.rs @@ -60,7 +60,9 @@ impl WasmBot { for (room_id, room) in response.rooms.join { for event in room.timeline.events { - if let Ok(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(ev))) = event.event.deserialize() { + if let Ok(AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(ev))) = + event.event.deserialize() + { self.on_room_message(&room_id, &ev).await } } @@ -81,19 +83,14 @@ pub async fn run() -> Result { let homeserver_url = Url::parse(&homeserver_url).unwrap(); let client = Client::new(homeserver_url).unwrap(); - client - .login(username, password, None, Some("rust-sdk-wasm")) - .await - .unwrap(); + client.login(username, password, None, Some("rust-sdk-wasm")).await.unwrap(); let bot = WasmBot(client.clone()); client.sync_once(SyncSettings::default()).await.unwrap(); let settings = SyncSettings::default().token(client.sync_token().await.unwrap()); - client - .sync_with_callback(settings, |response| bot.on_sync_response(response)) - .await; + client.sync_with_callback(settings, |response| bot.on_sync_response(response)).await; Ok(JsValue::NULL) } From aabda60e9f13d303db4b0ba1688704223943602b Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 14:10:23 +0200 Subject: [PATCH 43/88] Fix unused import warnings for wasm --- matrix_sdk/src/client.rs | 17 +++++++++-------- matrix_sdk/src/http_client.rs | 15 ++++++++------- matrix_sdk_crypto/src/verification/machine.rs | 4 +++- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 74b61ef5..4e5f1126 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -13,11 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(all(feature = "encryption", not(target_arch = "wasm32")))] +use std::path::PathBuf; #[cfg(feature = "encryption")] use std::{ collections::BTreeMap, io::{Cursor, Write}, - path::PathBuf, }; #[cfg(feature = "sso_login")] use std::{ @@ -39,10 +40,12 @@ use futures_timer::Delay as sleep; use http::HeaderValue; #[cfg(feature = "sso_login")] use http::Response; +#[cfg(all(feature = "encryption", not(target_arch = "wasm32")))] +use matrix_sdk_base::crypto::{decrypt_key_export, encrypt_key_export, olm::InboundGroupSession}; #[cfg(feature = "encryption")] use matrix_sdk_base::crypto::{ - decrypt_key_export, encrypt_key_export, olm::InboundGroupSession, store::CryptoStoreError, - AttachmentDecryptor, OutgoingRequests, RoomMessageRequest, ToDeviceRequest, + store::CryptoStoreError, AttachmentDecryptor, OutgoingRequests, RoomMessageRequest, + ToDeviceRequest, }; use matrix_sdk_base::{ deserialized_responses::SyncResponse, @@ -64,7 +67,7 @@ use tracing::{error, info, instrument}; use url::Url; #[cfg(feature = "sso_login")] use warp::Filter; -#[cfg(feature = "encryption")] +#[cfg(all(feature = "encryption", not(target_arch = "wasm32")))] use zeroize::Zeroizing; /// Enum controlling if a loop running callbacks should continue or abort. @@ -2427,8 +2430,7 @@ impl Client { /// .expect("Can't export keys."); /// # }); /// ``` - #[cfg(feature = "encryption")] - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(feature = "encryption", not(target_arch = "wasm32")))] #[cfg_attr(feature = "docs", doc(cfg(all(encryption, not(target_arch = "wasm32")))))] pub async fn export_keys( &self, @@ -2487,8 +2489,7 @@ impl Client { /// .expect("Can't import keys"); /// # }); /// ``` - #[cfg(feature = "encryption")] - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(feature = "encryption", not(target_arch = "wasm32")))] #[cfg_attr(feature = "docs", doc(cfg(all(encryption, not(target_arch = "wasm32")))))] pub async fn import_keys(&self, path: PathBuf, passphrase: &str) -> Result<(usize, usize)> { let olm = self.base_client.olm_machine().await.ok_or(Error::AuthenticationRequired)?; diff --git a/matrix_sdk/src/http_client.rs b/matrix_sdk/src/http_client.rs index 6874b90f..ed5477a4 100644 --- a/matrix_sdk/src/http_client.rs +++ b/matrix_sdk/src/http_client.rs @@ -12,16 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(all(not(target_arch = "wasm32")))] -use std::sync::atomic::{AtomicU64, Ordering}; use std::{convert::TryFrom, fmt::Debug, sync::Arc}; -#[cfg(all(not(target_arch = "wasm32")))] -use backoff::{future::retry, Error as RetryError, ExponentialBackoff}; use bytes::{Bytes, BytesMut}; -#[cfg(all(not(target_arch = "wasm32")))] -use http::StatusCode; -use http::{HeaderValue, Response as HttpResponse}; +use http::Response as HttpResponse; use matrix_sdk_common::{async_trait, locks::RwLock, AsyncTraitDeps}; use reqwest::{Client, Response}; use ruma::api::{ @@ -236,6 +230,8 @@ pub(crate) fn client_with_config(config: &ClientConfig) -> Result, config: RequestConfig, ) -> Result, HttpError> { + use std::sync::atomic::{AtomicU64, Ordering}; + + use backoff::{future::retry, Error as RetryError, ExponentialBackoff}; + use http::StatusCode; + let mut backoff = ExponentialBackoff::default(); let mut request = reqwest::Request::try_from(request)?; let retry_limit = config.retry_limit; diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 9a1162fe..b5d1d60c 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -16,7 +16,7 @@ use std::{convert::TryFrom, sync::Arc}; use dashmap::DashMap; use matrix_sdk_common::{locks::Mutex, uuid::Uuid}; -use ruma::{uint, DeviceId, MilliSecondsSinceUnixEpoch, UInt, UserId}; +use ruma::{DeviceId, MilliSecondsSinceUnixEpoch, UserId}; use tracing::{info, trace, warn}; use super::{ @@ -123,6 +123,8 @@ impl VerificationMachine { #[cfg(not(target_arch = "wasm32"))] fn is_timestamp_valid(timestamp: &MilliSecondsSinceUnixEpoch) -> bool { + use ruma::{uint, UInt}; + // The event should be ignored if the event is older than 10 minutes let old_timestamp_threshold: UInt = uint!(600); // The event should be ignored if the event is 5 minutes or more into the From 4d5768111d99d7c5b4319621d333233a11449b9b Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 14:48:55 +0200 Subject: [PATCH 44/88] Simplify ruma identifier imports --- matrix_sdk/src/client.rs | 2 +- matrix_sdk/src/room/joined.rs | 2 +- matrix_sdk/src/verification/sas.rs | 2 +- matrix_sdk_base/examples/state_inspector.rs | 5 +---- matrix_sdk_base/src/store/memory_store.rs | 8 +++----- matrix_sdk_base/src/store/sled_store/mod.rs | 6 ++++-- matrix_sdk_common/src/deserialized_responses.rs | 3 +-- matrix_sdk_crypto/src/identities/device.rs | 4 +--- matrix_sdk_crypto/src/identities/manager.rs | 5 ++--- matrix_sdk_crypto/src/key_request.rs | 2 +- matrix_sdk_crypto/src/machine.rs | 7 +++---- matrix_sdk_crypto/src/olm/account.rs | 7 ++----- matrix_sdk_crypto/src/olm/group_sessions/inbound.rs | 2 +- matrix_sdk_crypto/src/olm/session.rs | 2 +- matrix_sdk_crypto/src/store/mod.rs | 7 ++----- matrix_sdk_crypto/src/store/sled.rs | 5 ++--- matrix_sdk_crypto/src/verification/event_enums.rs | 3 +-- matrix_sdk_crypto/src/verification/qrcode.rs | 4 ++-- matrix_sdk_test/src/appservice.rs | 2 +- 19 files changed, 31 insertions(+), 47 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 4e5f1126..c977755c 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -56,7 +56,7 @@ use mime::{self, Mime}; #[cfg(feature = "sso_login")] use rand::{thread_rng, Rng}; use reqwest::header::InvalidHeaderValue; -use ruma::{api::SendAccessToken, events::AnyMessageEventContent, identifiers::MxcUri}; +use ruma::{api::SendAccessToken, events::AnyMessageEventContent, MxcUri}; #[cfg(feature = "sso_login")] use tokio::{net::TcpListener, sync::oneshot}; #[cfg(feature = "sso_login")] diff --git a/matrix_sdk/src/room/joined.rs b/matrix_sdk/src/room/joined.rs index e6fad924..2d5be644 100644 --- a/matrix_sdk/src/room/joined.rs +++ b/matrix_sdk/src/room/joined.rs @@ -38,8 +38,8 @@ use ruma::{ }, AnyMessageEventContent, AnyStateEventContent, }, - identifiers::{EventId, UserId}, receipt::ReceiptType, + EventId, UserId, }; #[cfg(feature = "encryption")] use tracing::instrument; diff --git a/matrix_sdk/src/verification/sas.rs b/matrix_sdk/src/verification/sas.rs index c2e90dfe..76d1df92 100644 --- a/matrix_sdk/src/verification/sas.rs +++ b/matrix_sdk/src/verification/sas.rs @@ -42,7 +42,7 @@ impl SasVerification { /// # use matrix_sdk::Client; /// # use futures::executor::block_on; /// # use url::Url; - /// # use ruma::identifiers::user_id; + /// # use ruma::user_id; /// use matrix_sdk::verification::SasVerification; /// use matrix_sdk_base::crypto::AcceptSettings; /// use matrix_sdk::ruma::events::key::verification::ShortAuthenticationString; diff --git a/matrix_sdk_base/examples/state_inspector.rs b/matrix_sdk_base/examples/state_inspector.rs index 5944104c..6aefc443 100644 --- a/matrix_sdk_base/examples/state_inspector.rs +++ b/matrix_sdk_base/examples/state_inspector.rs @@ -6,10 +6,7 @@ use atty::Stream; use clap::{App as Argparse, AppSettings as ArgParseSettings, Arg, ArgMatches, SubCommand}; use futures::executor::block_on; use matrix_sdk_base::{RoomInfo, Store}; -use ruma::{ - events::EventType, - identifiers::{RoomId, UserId}, -}; +use ruma::{events::EventType, RoomId, UserId}; #[cfg(not(target_arch = "wasm32"))] use rustyline::{ completion::{Completer, Pair}, diff --git a/matrix_sdk_base/src/store/memory_store.rs b/matrix_sdk_base/src/store/memory_store.rs index 627d5b9a..6c919a4b 100644 --- a/matrix_sdk_base/src/store/memory_store.rs +++ b/matrix_sdk_base/src/store/memory_store.rs @@ -28,9 +28,9 @@ use ruma::{ AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent, EventType, }, - identifiers::{EventId, MxcUri, RoomId, UserId}, receipt::ReceiptType, serde::Raw, + EventId, MxcUri, RoomId, UserId, }; use tracing::info; @@ -562,10 +562,8 @@ impl StateStore for MemoryStore { mod test { use matrix_sdk_test::async_test; use ruma::{ - api::client::r0::media::get_content_thumbnail::Method, - identifiers::{event_id, mxc_uri, room_id, user_id, UserId}, - receipt::ReceiptType, - uint, + api::client::r0::media::get_content_thumbnail::Method, event_id, mxc_uri, + receipt::ReceiptType, room_id, uint, user_id, UserId, }; use serde_json::json; diff --git a/matrix_sdk_base/src/store/sled_store/mod.rs b/matrix_sdk_base/src/store/sled_store/mod.rs index 31a9ccbd..c1f2baef 100644 --- a/matrix_sdk_base/src/store/sled_store/mod.rs +++ b/matrix_sdk_base/src/store/sled_store/mod.rs @@ -903,6 +903,7 @@ mod test { use matrix_sdk_test::async_test; use ruma::{ api::client::r0::media::get_content_thumbnail::Method, + event_id, events::{ room::{ member::{MemberEventContent, MembershipState}, @@ -910,10 +911,11 @@ mod test { }, AnySyncStateEvent, EventType, Unsigned, }, - identifiers::{event_id, mxc_uri, room_id, user_id, EventId, UserId}, + mxc_uri, receipt::ReceiptType, + room_id, serde::Raw, - uint, MilliSecondsSinceUnixEpoch, + uint, user_id, EventId, MilliSecondsSinceUnixEpoch, UserId, }; use serde_json::json; diff --git a/matrix_sdk_common/src/deserialized_responses.rs b/matrix_sdk_common/src/deserialized_responses.rs index 1a8c28af..24b07dd8 100644 --- a/matrix_sdk_common/src/deserialized_responses.rs +++ b/matrix_sdk_common/src/deserialized_responses.rs @@ -12,9 +12,8 @@ use ruma::{ room::member::MemberEventContent, AnySyncRoomEvent, StateEvent, StrippedStateEvent, SyncStateEvent, Unsigned, }, - identifiers::{DeviceKeyAlgorithm, EventId, RoomId, UserId}, serde::Raw, - DeviceIdBox, MilliSecondsSinceUnixEpoch, + DeviceIdBox, DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use serde::{Deserialize, Serialize}; diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index f57e62ff..fecccaa2 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -30,9 +30,7 @@ use ruma::{ forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, room::encrypted::EncryptedEventContent, AnyToDeviceEventContent, }, - identifiers::{ - DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId, - }, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{json, Value}; diff --git a/matrix_sdk_crypto/src/identities/manager.rs b/matrix_sdk_crypto/src/identities/manager.rs index 0af468b7..e8e7fedc 100644 --- a/matrix_sdk_crypto/src/identities/manager.rs +++ b/matrix_sdk_crypto/src/identities/manager.rs @@ -21,9 +21,8 @@ use std::{ use futures::future::join_all; use matrix_sdk_common::executor::spawn; use ruma::{ - api::client::r0::keys::get_keys::Response as KeysQueryResponse, - encryption::DeviceKeys, - identifiers::{DeviceId, DeviceIdBox, UserId}, + api::client::r0::keys::get_keys::Response as KeysQueryResponse, encryption::DeviceKeys, + DeviceId, DeviceIdBox, UserId, }; use tracing::{trace, warn}; diff --git a/matrix_sdk_crypto/src/key_request.rs b/matrix_sdk_crypto/src/key_request.rs index ba3d1287..d089853f 100644 --- a/matrix_sdk_crypto/src/key_request.rs +++ b/matrix_sdk_crypto/src/key_request.rs @@ -30,8 +30,8 @@ use ruma::{ room_key_request::{Action, RequestedKeyInfo, RoomKeyRequestToDeviceEventContent}, AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent, }, - identifiers::{DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId}, to_device::DeviceIdOrAllDevices, + DeviceId, DeviceIdBox, EventEncryptionAlgorithm, RoomId, UserId, }; use serde::{Deserialize, Serialize}; use thiserror::Error; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 46763f5a..cb02d64e 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -1235,6 +1235,7 @@ pub(crate) mod test { IncomingResponse, }, encryption::OneTimeKey, + event_id, events::{ dummy::DummyToDeviceEventContent, room::{ @@ -1244,10 +1245,8 @@ pub(crate) mod test { AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, AnyToDeviceEvent, AnyToDeviceEventContent, SyncMessageEvent, ToDeviceEvent, Unsigned, }, - identifiers::{ - event_id, room_id, user_id, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId, - }, - uint, MilliSecondsSinceUnixEpoch, + room_id, uint, user_id, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, + MilliSecondsSinceUnixEpoch, UserId, }; use serde_json::json; diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index 718080fb..b7292e85 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -37,12 +37,9 @@ use ruma::{ room::encrypted::{EncryptedEventContent, EncryptedEventScheme}, AnyToDeviceEvent, ToDeviceEvent, }, - identifiers::{ - DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, - UserId, - }, serde::{CanonicalJsonValue, Raw}, - UInt, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UInt, + UserId, }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; diff --git a/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs b/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs index 75b6529b..f4babb4f 100644 --- a/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs +++ b/matrix_sdk_crypto/src/olm/group_sessions/inbound.rs @@ -32,8 +32,8 @@ use ruma::{ }, AnySyncRoomEvent, SyncMessageEvent, }, - identifiers::{DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId}, serde::Raw, + DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, }; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/matrix_sdk_crypto/src/olm/session.rs b/matrix_sdk_crypto/src/olm/session.rs index 10fdd5ca..a34d901f 100644 --- a/matrix_sdk_crypto/src/olm/session.rs +++ b/matrix_sdk_crypto/src/olm/session.rs @@ -28,7 +28,7 @@ use ruma::{ }, AnyToDeviceEventContent, EventContent, }, - identifiers::{DeviceId, DeviceKeyAlgorithm, UserId}, + DeviceId, DeviceKeyAlgorithm, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::json; diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 022c5c03..37c413cb 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -56,11 +56,8 @@ pub use memorystore::MemoryStore; use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError}; pub use pickle_key::{EncryptedPickleKey, PickleKey}; use ruma::{ - events::room_key_request::RequestedKeyInfo, - identifiers::{ - DeviceId, DeviceIdBox, DeviceKeyAlgorithm, Error as IdentifierValidationError, RoomId, - UserId, - }, + events::room_key_request::RequestedKeyInfo, identifiers::Error as IdentifierValidationError, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, RoomId, UserId, }; use serde_json::Error as SerdeError; use thiserror::Error; diff --git a/matrix_sdk_crypto/src/store/sled.rs b/matrix_sdk_crypto/src/store/sled.rs index 8dd547c7..b803496c 100644 --- a/matrix_sdk_crypto/src/store/sled.rs +++ b/matrix_sdk_crypto/src/store/sled.rs @@ -757,9 +757,8 @@ mod test { use matrix_sdk_test::async_test; use olm_rs::outbound_group_session::OlmOutboundGroupSession; use ruma::{ - encryption::SignedKey, - events::room_key_request::RequestedKeyInfo, - identifiers::{room_id, user_id, DeviceId, EventEncryptionAlgorithm, UserId}, + encryption::SignedKey, events::room_key_request::RequestedKeyInfo, room_id, user_id, + DeviceId, EventEncryptionAlgorithm, UserId, }; use tempfile::tempdir; diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index 2c28feb7..39b9d195 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -33,9 +33,8 @@ use ruma::{ room::message::{KeyVerificationRequestEventContent, MessageType}, AnyMessageEvent, AnyMessageEventContent, AnyToDeviceEvent, AnyToDeviceEventContent, }, - identifiers::{DeviceId, RoomId, UserId}, serde::CanonicalJsonValue, - MilliSecondsSinceUnixEpoch, + DeviceId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use super::FlowId; diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 003a75de..37a60917 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -33,7 +33,7 @@ use ruma::{ }, AnyMessageEventContent, AnyToDeviceEventContent, }, - identifiers::{DeviceIdBox, DeviceKeyAlgorithm, UserId}, + DeviceIdBox, DeviceKeyAlgorithm, UserId, }; use thiserror::Error; @@ -696,7 +696,7 @@ mod test { use matrix_qrcode::QrVerificationData; use matrix_sdk_test::async_test; - use ruma::identifiers::{event_id, room_id, user_id, DeviceIdBox, UserId}; + use ruma::{event_id, room_id, user_id, DeviceIdBox, UserId}; use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, diff --git a/matrix_sdk_test/src/appservice.rs b/matrix_sdk_test/src/appservice.rs index 23d889b2..8853613a 100644 --- a/matrix_sdk_test/src/appservice.rs +++ b/matrix_sdk_test/src/appservice.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use ruma::{events::AnyRoomEvent, identifiers::room_id}; +use ruma::{events::AnyRoomEvent, room_id}; use serde_json::Value; use crate::{test_json, EventsJson}; From 52fd551890df658ed0b42bfa7d1395c1b3aeabf2 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 23 Jun 2021 15:01:52 +0200 Subject: [PATCH 45/88] appservice: Reexport ruma entirely --- matrix_sdk_appservice/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_appservice/src/lib.rs b/matrix_sdk_appservice/src/lib.rs index b4da1e55..273240ec 100644 --- a/matrix_sdk_appservice/src/lib.rs +++ b/matrix_sdk_appservice/src/lib.rs @@ -89,14 +89,15 @@ use dashmap::DashMap; pub use error::Error; use http::{uri::PathAndQuery, Uri}; pub use matrix_sdk; +#[doc(no_inline)] +pub use matrix_sdk::ruma; use matrix_sdk::{ bytes::Bytes, reqwest::Url, Client, ClientConfig, EventHandler, HttpError, Session, }; use regex::Regex; -#[doc(inline)] -pub use ruma::api::{appservice as api, appservice::Registration}; use ruma::{ api::{ + appservice::Registration, client::{ error::ErrorKind, r0::{account::register, uiaa::UiaaResponse}, From df71de5af24fb08477801445042d0cdd55020012 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 25 Jun 2021 12:04:20 +0200 Subject: [PATCH 46/88] crypto: Store key::verification::start::SasV1Content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … instead of SasV1ContentInit --- .../src/verification/sas/sas_state.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 2c63bbc3..2ab1beb6 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -13,7 +13,7 @@ // limitations under the License. use std::{ - convert::TryFrom, + convert::{TryFrom, TryInto}, matches, sync::{Arc, Mutex}, time::{Duration, Instant}, @@ -222,7 +222,7 @@ impl std::fmt::Debug for SasState { /// The initial SAS state. #[derive(Clone, Debug)] pub struct Created { - protocol_definitions: SasV1ContentInit, + protocol_definitions: SasV1Content, } /// The initial SAS state if the other side started the SAS verification. @@ -407,7 +407,9 @@ impl SasState { key_agreement_protocols: KEY_AGREEMENT_PROTOCOLS.to_vec(), message_authentication_codes: MACS.to_vec(), hashes: HASHES.to_vec(), - }, + } + .try_into() + .expect("Invalid protocol definition."), }), } } @@ -417,19 +419,13 @@ impl SasState { FlowId::ToDevice(s) => OwnedStartContent::ToDevice(StartToDeviceEventContent::new( self.device_id().into(), s.to_string(), - StartMethod::SasV1( - SasV1Content::new(self.state.protocol_definitions.clone()) - .expect("Invalid initial protocol definitions."), - ), + StartMethod::SasV1(self.state.protocol_definitions.clone()), )), FlowId::InRoom(r, e) => OwnedStartContent::Room( r.clone(), StartEventContent::new( self.device_id().into(), - StartMethod::SasV1( - SasV1Content::new(self.state.protocol_definitions.clone()) - .expect("Invalid initial protocol definitions."), - ), + StartMethod::SasV1(self.state.protocol_definitions.clone()), Relation::new(e.clone()), ), ), From f454d407af8444ff8541de1aa58728335347062c Mon Sep 17 00:00:00 2001 From: Johannes Becker Date: Tue, 29 Jun 2021 12:09:55 +0200 Subject: [PATCH 47/88] appservice: Drop actix --- .github/workflows/ci.yml | 9 -- matrix_sdk_appservice/Cargo.toml | 5 +- matrix_sdk_appservice/src/error.rs | 11 -- matrix_sdk_appservice/src/lib.rs | 30 +--- matrix_sdk_appservice/src/webserver/actix.rs | 149 ------------------- matrix_sdk_appservice/src/webserver/mod.rs | 2 - matrix_sdk_appservice/tests/tests.rs | 82 +--------- 7 files changed, 5 insertions(+), 283 deletions(-) delete mode 100644 matrix_sdk_appservice/src/webserver/actix.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 059d5a26..517a939b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,19 +94,10 @@ jobs: strategy: matrix: name: - - linux / appservice / stable / actix - - macOS / appservice / stable / actix - linux / appservice / stable / warp - macOS / appservice / stable / warp include: - - name: linux / appservice / stable / actix - cargo_args: --no-default-features --features actix - - - name: macOS / appservice / stable / actix - os: macOS-latest - cargo_args: --no-default-features --features actix - - name: linux / appservice / stable / warp cargo_args: --features warp diff --git a/matrix_sdk_appservice/Cargo.toml b/matrix_sdk_appservice/Cargo.toml index 4a14ea74..bb1d41c5 100644 --- a/matrix_sdk_appservice/Cargo.toml +++ b/matrix_sdk_appservice/Cargo.toml @@ -9,13 +9,10 @@ version = "0.1.0" [features] default = ["warp"] -actix = ["actix-rt", "actix-web"] -docs = ["actix", "warp"] +docs = ["warp"] [dependencies] -actix-rt = { version = "2", optional = true } -actix-web = { version = "4.0.0-beta.6", optional = true } dashmap = "4" futures = "0.3" futures-util = "0.3" diff --git a/matrix_sdk_appservice/src/error.rs b/matrix_sdk_appservice/src/error.rs index 1e08c192..3bd1d4f0 100644 --- a/matrix_sdk_appservice/src/error.rs +++ b/matrix_sdk_appservice/src/error.rs @@ -70,19 +70,8 @@ pub enum Error { #[cfg(feature = "warp")] #[error("warp rejection: {0}")] WarpRejection(String), - - #[cfg(feature = "actix")] - #[error(transparent)] - Actix(#[from] actix_web::Error), - - #[cfg(feature = "actix")] - #[error(transparent)] - ActixPayload(#[from] actix_web::error::PayloadError), } -#[cfg(feature = "actix")] -impl actix_web::error::ResponseError for Error {} - #[cfg(feature = "warp")] impl warp::reject::Reject for Error {} diff --git a/matrix_sdk_appservice/src/lib.rs b/matrix_sdk_appservice/src/lib.rs index 273240ec..125cb7f0 100644 --- a/matrix_sdk_appservice/src/lib.rs +++ b/matrix_sdk_appservice/src/lib.rs @@ -74,8 +74,8 @@ //! [matrix-org/matrix-rust-sdk#228]: https://github.com/matrix-org/matrix-rust-sdk/issues/228 //! [examples directory]: https://github.com/matrix-org/matrix-rust-sdk/tree/master/matrix_sdk_appservice/examples -#[cfg(not(any(feature = "actix", feature = "warp")))] -compile_error!("one webserver feature must be enabled. available ones: `actix`, `warp`"); +#[cfg(not(any(feature = "warp")))] +compile_error!("one webserver feature must be enabled. available ones: `warp`"); use std::{ convert::{TryFrom, TryInto}, @@ -441,24 +441,6 @@ impl AppService { Ok(false) } - /// Returns a closure to be used with [`actix_web::App::configure()`] - /// - /// Note that if you handle any of the [application-service-specific - /// routes], including the legacy routes, you will break the appservice - /// functionality. - /// - /// [application-service-specific routes]: https://spec.matrix.org/unstable/application-service-api/#legacy-routes - #[cfg(feature = "actix")] - #[cfg_attr(docs, doc(cfg(feature = "actix")))] - pub fn actix_configure(&self) -> impl FnOnce(&mut actix_web::web::ServiceConfig) { - let appservice = self.clone(); - - move |config| { - config.data(appservice); - webserver::actix::configure(config); - } - } - /// Returns a [`warp::Filter`] to be used as [`warp::serve()`] route /// /// Note that if you handle any of the [application-service-specific @@ -482,19 +464,13 @@ impl AppService { let port = port.into(); info!("Starting AppService on {}:{}", &host, &port); - #[cfg(feature = "actix")] - { - webserver::actix::run_server(self.clone(), host, port).await?; - Ok(()) - } - #[cfg(feature = "warp")] { webserver::warp::run_server(self.clone(), host, port).await?; Ok(()) } - #[cfg(not(any(feature = "actix", feature = "warp",)))] + #[cfg(not(any(feature = "warp",)))] unreachable!() } } diff --git a/matrix_sdk_appservice/src/webserver/actix.rs b/matrix_sdk_appservice/src/webserver/actix.rs deleted file mode 100644 index 5c22643d..00000000 --- a/matrix_sdk_appservice/src/webserver/actix.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2021 Famedly GmbH -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::pin::Pin; - -pub use actix_web::Scope; -use actix_web::{ - dev::Payload, - error::PayloadError, - get, put, - web::{self, BytesMut, Data}, - App, FromRequest, HttpRequest, HttpResponse, HttpServer, -}; -use futures::Future; -use futures_util::TryStreamExt; -use ruma::api::appservice as api; - -use crate::{error::Error, AppService}; - -pub async fn run_server( - appservice: AppService, - host: impl Into, - port: impl Into, -) -> Result<(), Error> { - HttpServer::new(move || App::new().configure(appservice.actix_configure())) - .bind((host.into(), port.into()))? - .run() - .await?; - - Ok(()) -} - -pub fn configure(config: &mut actix_web::web::ServiceConfig) { - // also handles legacy routes - config.service(push_transactions).service(query_user_id).service(query_room_alias).service( - web::scope("/_matrix/app/v1") - .service(push_transactions) - .service(query_user_id) - .service(query_room_alias), - ); -} - -#[tracing::instrument] -#[put("/transactions/{txn_id}")] -async fn push_transactions( - request: IncomingRequest, - appservice: Data, -) -> Result { - if !appservice.compare_hs_token(request.access_token) { - return Ok(HttpResponse::Unauthorized().finish()); - } - - appservice.get_cached_client(None)?.receive_transaction(request.incoming).await?; - - Ok(HttpResponse::Ok().json("{}")) -} - -#[tracing::instrument] -#[get("/users/{user_id}")] -async fn query_user_id( - request: IncomingRequest, - appservice: Data, -) -> Result { - if !appservice.compare_hs_token(request.access_token) { - return Ok(HttpResponse::Unauthorized().finish()); - } - - Ok(HttpResponse::Ok().json("{}")) -} - -#[tracing::instrument] -#[get("/rooms/{room_alias}")] -async fn query_room_alias( - request: IncomingRequest, - appservice: Data, -) -> Result { - if !appservice.compare_hs_token(request.access_token) { - return Ok(HttpResponse::Unauthorized().finish()); - } - - Ok(HttpResponse::Ok().json("{}")) -} - -#[derive(Debug)] -pub struct IncomingRequest { - access_token: String, - incoming: T, -} - -impl FromRequest for IncomingRequest { - type Error = Error; - type Future = Pin>>>; - type Config = (); - - fn from_request(request: &HttpRequest, payload: &mut Payload) -> Self::Future { - let request = request.to_owned(); - let payload = payload.take(); - - Box::pin(async move { - let mut builder = - http::request::Builder::new().method(request.method()).uri(request.uri()); - - let headers = builder.headers_mut().ok_or(Error::UnknownHttpRequestBuilder)?; - for (key, value) in request.headers().iter() { - headers.append(key, value.to_owned()); - } - - let bytes = payload - .try_fold(BytesMut::new(), |mut body, chunk| async move { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) - .await? - .into(); - - let access_token = match request.uri().query() { - Some(query) => { - let query: Vec<(String, String)> = ruma::serde::urlencoded::from_str(query)?; - query.into_iter().find(|(key, _)| key == "access_token").map(|(_, value)| value) - } - None => None, - }; - - let access_token = match access_token { - Some(access_token) => access_token, - None => return Err(Error::MissingAccessToken), - }; - - let request = builder.body(bytes)?; - let request = crate::transform_legacy_route(request)?; - - Ok(IncomingRequest { - access_token, - incoming: ruma::api::IncomingRequest::try_from_http_request(request)?, - }) - }) - } -} diff --git a/matrix_sdk_appservice/src/webserver/mod.rs b/matrix_sdk_appservice/src/webserver/mod.rs index a0880ec6..0b8d1141 100644 --- a/matrix_sdk_appservice/src/webserver/mod.rs +++ b/matrix_sdk_appservice/src/webserver/mod.rs @@ -1,4 +1,2 @@ -#[cfg(feature = "actix")] -pub mod actix; #[cfg(feature = "warp")] pub mod warp; diff --git a/matrix_sdk_appservice/tests/tests.rs b/matrix_sdk_appservice/tests/tests.rs index 23dbd221..aebb04f8 100644 --- a/matrix_sdk_appservice/tests/tests.rs +++ b/matrix_sdk_appservice/tests/tests.rs @@ -1,7 +1,5 @@ use std::sync::{Arc, Mutex}; -#[cfg(feature = "actix")] -use actix_web::{test as actix_test, App as ActixApp, HttpResponse}; use matrix_sdk::{ async_trait, room::Room, @@ -24,7 +22,7 @@ fn registration_string() -> String { async fn appservice(registration: Option) -> Result { // env::set_var( // "RUST_LOG", - // "mockito=debug,matrix_sdk=debug,ruma=debug,actix_web=debug,warp=debug", + // "mockito=debug,matrix_sdk=debug,ruma=debug,warp=debug", // ); let _ = tracing_subscriber::fmt::try_init(); @@ -99,16 +97,6 @@ async fn test_put_transaction() -> Result<()> { .into_response() .status(); - #[cfg(feature = "actix")] - let status = { - let app = - actix_test::init_service(ActixApp::new().configure(appservice.actix_configure())).await; - - let req = actix_test::TestRequest::put().uri(uri).set_json(&transaction).to_request(); - - actix_test::call_service(&app, req).await.status() - }; - assert_eq!(status, 200); Ok(()) @@ -130,16 +118,6 @@ async fn test_get_user() -> Result<()> { .into_response() .status(); - #[cfg(feature = "actix")] - let status = { - let app = - actix_test::init_service(ActixApp::new().configure(appservice.actix_configure())).await; - - let req = actix_test::TestRequest::get().uri(uri).to_request(); - - actix_test::call_service(&app, req).await.status() - }; - assert_eq!(status, 200); Ok(()) @@ -161,16 +139,6 @@ async fn test_get_room() -> Result<()> { .into_response() .status(); - #[cfg(feature = "actix")] - let status = { - let app = - actix_test::init_service(ActixApp::new().configure(appservice.actix_configure())).await; - - let req = actix_test::TestRequest::get().uri(uri).to_request(); - - actix_test::call_service(&app, req).await.status() - }; - assert_eq!(status, 200); Ok(()) @@ -197,16 +165,6 @@ async fn test_invalid_access_token() -> Result<()> { .into_response() .status(); - #[cfg(feature = "actix")] - let status = { - let app = - actix_test::init_service(ActixApp::new().configure(appservice.actix_configure())).await; - - let req = actix_test::TestRequest::put().uri(uri).set_json(&transaction).to_request(); - - actix_test::call_service(&app, req).await.status() - }; - assert_eq!(status, 401); Ok(()) @@ -237,20 +195,6 @@ async fn test_no_access_token() -> Result<()> { assert_eq!(status, 401); } - #[cfg(feature = "actix")] - { - let app = - actix_test::init_service(ActixApp::new().configure(appservice.actix_configure())).await; - - let req = actix_test::TestRequest::put().uri(uri).set_json(&transaction).to_request(); - - let resp = actix_test::call_service(&app, req).await; - - // TODO: this should actually return a 401 but is 500 because something in the - // extractor fails - assert_eq!(resp.status(), 500); - } - Ok(()) } @@ -296,16 +240,6 @@ async fn test_event_handler() -> Result<()> { .await .unwrap(); - #[cfg(feature = "actix")] - { - let app = - actix_test::init_service(ActixApp::new().configure(appservice.actix_configure())).await; - - let req = actix_test::TestRequest::put().uri(uri).set_json(&transaction).to_request(); - - actix_test::call_service(&app, req).await; - }; - let on_room_member_called = *example.on_state_member.lock().unwrap(); assert!(on_room_member_called); @@ -332,20 +266,6 @@ async fn test_unrelated_path() -> Result<()> { response.status() }; - #[cfg(feature = "actix")] - let status = { - let app = actix_test::init_service( - ActixApp::new() - .configure(appservice.actix_configure()) - .route("/unrelated", actix_web::web::get().to(HttpResponse::Ok)), - ) - .await; - - let req = actix_test::TestRequest::get().uri("/unrelated").to_request(); - - actix_test::call_service(&app, req).await.status() - }; - assert_eq!(status, 200); Ok(()) From c78406ceb937229db834a0539eb5264f98e891a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 24 Jun 2021 12:12:13 +0200 Subject: [PATCH 48/88] crypto: Clean up the method to check if a device is verified. --- matrix_sdk/examples/emoji_verification.rs | 2 +- matrix_sdk/src/client.rs | 2 +- matrix_sdk/src/device.rs | 7 +- matrix_sdk_crypto/src/identities/device.rs | 86 +++++++++++--------- matrix_sdk_crypto/src/identities/user.rs | 16 ++-- matrix_sdk_crypto/src/key_request.rs | 2 +- matrix_sdk_crypto/src/machine.rs | 10 +-- matrix_sdk_crypto/src/verification/qrcode.rs | 4 +- 8 files changed, 68 insertions(+), 61 deletions(-) diff --git a/matrix_sdk/examples/emoji_verification.rs b/matrix_sdk/examples/emoji_verification.rs index 90c7fd43..1307d9a2 100644 --- a/matrix_sdk/examples/emoji_verification.rs +++ b/matrix_sdk/examples/emoji_verification.rs @@ -58,7 +58,7 @@ async fn print_devices(user_id: &UserId, client: &Client) { " {:<10} {:<30} {:<}", device.device_id(), device.display_name().as_deref().unwrap_or_default(), - device.is_trusted() + device.verified() ); } } diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index c977755c..3d1d2713 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -2251,7 +2251,7 @@ impl Client { /// .unwrap() /// .unwrap(); /// - /// println!("{:?}", device.is_trusted()); + /// println!("{:?}", device.verified()); /// /// let verification = device.start_verification().await.unwrap(); /// # }); diff --git a/matrix_sdk/src/device.rs b/matrix_sdk/src/device.rs index c9e329c6..92c4fdaa 100644 --- a/matrix_sdk/src/device.rs +++ b/matrix_sdk/src/device.rs @@ -69,9 +69,10 @@ impl Device { Ok(SasVerification { inner: sas, client: self.client.clone() }) } - /// Is the device trusted. - pub fn is_trusted(&self) -> bool { - self.inner.trust_state() + /// Is the device considered to be verified, either by locally trusting it + /// or using cross signing. + pub fn verified(&self) -> bool { + self.inner.verified() } /// Set the local trust state of the device to the given state. diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index fecccaa2..d9f6e8cc 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -145,9 +145,20 @@ impl Device { } } - /// Get the trust state of the device. - pub fn trust_state(&self) -> bool { - self.inner.trust_state(&self.own_identity, &self.device_owner_identity) + /// Is this device considered to be verified. + /// + /// This method returns true if either [`is_locally_trusted()`] returns true + /// or if [`is_cross_signing_trusted()`] returns true. + /// + /// [`is_locally_trusted()`]: #method.is_locally_trusted + /// [`is_cross_signing_trusted()`]: #method.is_cross_signing_trusted + pub fn verified(&self) -> bool { + self.inner.verified(&self.own_identity, &self.device_owner_identity) + } + + /// Is this device considered to be verified using cross signing. + pub fn is_cross_signing_trusted(&self) -> bool { + self.inner.is_cross_signing_trusted(&self.own_identity, &self.device_owner_identity) } /// Set the local trust state of the device to the given state. @@ -236,7 +247,7 @@ impl UserDevices { /// Returns true if there is at least one devices of this user that is /// considered to be verified, false otherwise. pub fn is_any_verified(&self) -> bool { - self.inner.values().any(|d| d.trust_state(&self.own_identity, &self.device_owner_identity)) + self.inner.values().any(|d| d.verified(&self.own_identity, &self.device_owner_identity)) } /// Iterator over all the device ids of the user devices. @@ -340,7 +351,7 @@ impl ReadOnlyDevice { } /// Is the device locally marked as trusted. - pub fn is_trusted(&self) -> bool { + pub fn is_locally_trusted(&self) -> bool { self.local_trust_state() == LocalTrust::Verified } @@ -369,46 +380,41 @@ impl ReadOnlyDevice { self.deleted.load(Ordering::Relaxed) } - pub(crate) fn trust_state( + pub(crate) fn verified( &self, own_identity: &Option, device_owner: &Option, ) -> bool { - // TODO we want to return an enum mentioning if the trust is local, if - // only the identity is trusted, if the identity and the device are - // trusted. - if self.is_trusted() { - // If the device is locally marked as verified just return so, no - // need to check signatures. - true - } else { - own_identity.as_ref().map_or(false, |own_identity| { - // Our own identity needs to be marked as verified. - own_identity.is_verified() - && device_owner - .as_ref() - .map(|device_identity| match device_identity { - // If it's one of our own devices, just check that - // we signed the device. - UserIdentities::Own(_) => { - own_identity.is_device_signed(self).map_or(false, |_| true) - } + self.is_locally_trusted() || self.is_cross_signing_trusted(own_identity, device_owner) + } - // If it's a device from someone else, first check - // that our user has signed the other user and then - // check if the other user has signed this device. - UserIdentities::Other(device_identity) => { - own_identity - .is_identity_signed(device_identity) - .map_or(false, |_| true) - && device_identity - .is_device_signed(self) - .map_or(false, |_| true) - } - }) - .unwrap_or(false) - }) - } + pub(crate) fn is_cross_signing_trusted( + &self, + own_identity: &Option, + device_owner: &Option, + ) -> bool { + own_identity.as_ref().map_or(false, |own_identity| { + // Our own identity needs to be marked as verified. + own_identity.is_verified() + && device_owner + .as_ref() + .map(|device_identity| match device_identity { + // If it's one of our own devices, just check that + // we signed the device. + UserIdentities::Own(_) => { + own_identity.is_device_signed(self).map_or(false, |_| true) + } + + // If it's a device from someone else, first check + // that our user has signed the other user and then + // check if the other user has signed this device. + UserIdentities::Other(device_identity) => { + own_identity.is_identity_signed(device_identity).map_or(false, |_| true) + && device_identity.is_device_signed(self).map_or(false, |_| true) + } + }) + .unwrap_or(false) + }) } pub(crate) async fn encrypt( diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index e983f786..184d64c2 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -784,15 +784,15 @@ pub(crate) mod test { device_owner_identity: Some(UserIdentities::Own(identity.clone())), }; - assert!(!second.trust_state()); - assert!(!second.is_trusted()); + assert!(!second.is_locally_trusted()); + assert!(!second.is_cross_signing_trusted()); - assert!(!first.trust_state()); - assert!(!first.is_trusted()); + assert!(!first.is_locally_trusted()); + assert!(!first.is_cross_signing_trusted()); identity.mark_as_verified(); - assert!(second.trust_state()); - assert!(!first.trust_state()); + assert!(second.verified()); + assert!(!first.verified()); } #[async_test] @@ -821,12 +821,12 @@ pub(crate) mod test { device_owner_identity: Some(public_identity.clone().into()), }; - assert!(!device.trust_state()); + assert!(!device.verified()); let mut device_keys = device.as_device_keys(); identity.sign_device_keys(&mut device_keys).await.unwrap(); device.inner.signatures = Arc::new(device_keys.signatures); - assert!(device.trust_state()); + assert!(device.verified()); } } diff --git a/matrix_sdk_crypto/src/key_request.rs b/matrix_sdk_crypto/src/key_request.rs index d089853f..570837d0 100644 --- a/matrix_sdk_crypto/src/key_request.rs +++ b/matrix_sdk_crypto/src/key_request.rs @@ -479,7 +479,7 @@ impl KeyRequestMachine { .flatten(); let own_device_check = || { - if device.trust_state() { + if device.verified() { Ok(None) } else { Err(KeyshareDecision::UntrustedDevice) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index cb02d64e..75b1fd40 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -928,7 +928,7 @@ impl OlmMachine { .unwrap_or(false) }) { if (self.user_id() == device.user_id() && self.device_id() == device.device_id()) - || device.is_trusted() + || device.verified() { VerificationState::Trusted } else { @@ -1771,7 +1771,7 @@ pub(crate) mod test { let bob_device = alice.get_device(bob.user_id(), bob.device_id()).await.unwrap().unwrap(); - assert!(!bob_device.is_trusted()); + assert!(!bob_device.verified()); let (alice_sas, request) = bob_device.start_verification().await.unwrap(); @@ -1834,14 +1834,14 @@ pub(crate) mod test { .unwrap(); assert!(alice_sas.is_done()); - assert!(bob_device.is_trusted()); + assert!(bob_device.verified()); let alice_device = bob.get_device(alice.user_id(), alice.device_id()).await.unwrap().unwrap(); - assert!(!alice_device.is_trusted()); + assert!(!alice_device.verified()); bob.handle_verification_event(&event).await; assert!(bob_sas.is_done()); - assert!(alice_device.is_trusted()); + assert!(alice_device.verified()); } } diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 37a60917..33578544 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -871,8 +871,8 @@ mod test { let identity = identity.own().unwrap(); - assert!(!bob_device.is_trusted()); - assert!(alice_device.is_trusted()); + assert!(!bob_device.is_locally_trusted()); + assert!(alice_device.is_locally_trusted()); assert!(identity.is_verified()); }; From 9a685d685c35930bc33477cb2cda6e7325791a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 24 Jun 2021 12:13:06 +0200 Subject: [PATCH 49/88] crypto: Add a couple more getters for the SAS verification --- matrix_sdk_crypto/src/verification/sas/inner_sas.rs | 12 ++++++++++++ matrix_sdk_crypto/src/verification/sas/mod.rs | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 67b5302e..7ca37d5a 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -312,6 +312,18 @@ impl InnerSas { matches!(self, InnerSas::Cancelled(_)) } + pub fn have_we_confirmed(&self) -> bool { + matches!(self, InnerSas::Confirmed(_) | InnerSas::WaitingForDone(_) | InnerSas::Done(_)) + } + + pub fn cancel_code(&self) -> Option { + if let InnerSas::Cancelled(c) = self { + Some(c.state.cancel_code.clone()) + } else { + None + } + } + pub fn timed_out(&self) -> bool { match self { InnerSas::Created(s) => s.timed_out(), diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index fb9a5be6..3339c8e4 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -104,6 +104,16 @@ impl Sas { self.identities_being_verified.is_self_verification() } + /// Have we confirmed that the short auth string matches. + pub fn have_we_confirmed(&self) -> bool { + self.inner.lock().unwrap().have_we_confirmed() + } + + /// Get the cancel code of this SAS verification if it has been cancelled + pub fn cancel_code(&self) -> Option { + self.inner.lock().unwrap().cancel_code() + } + #[cfg(test)] #[allow(dead_code)] pub(crate) fn set_creation_time(&self, time: Instant) { From 55690ddd54137639457755bfc573b3a4dfbbd4dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 24 Jun 2021 12:14:09 +0200 Subject: [PATCH 50/88] crypto: Allow canceling a SAS verification with a specific cancel code --- matrix_sdk_crypto/src/verification/sas/mod.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 3339c8e4..471be4aa 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -359,7 +359,20 @@ impl Sas { self.cancel_with_code(CancelCode::User) } - pub(crate) fn cancel_with_code(&self, code: CancelCode) -> Option { + /// Cancel the verification. + /// + /// This cancels the verification with given `CancelCode`. + /// + /// **Note**: This method should generally not be used, the [`cancel()`] + /// method should be preferred. The SDK will automatically cancel with the + /// approprate cancel code, user initiated cancellations should only cancel + /// with the `CancelCode::User` + /// + /// Returns None if the `Sas` object is already in a canceled state, + /// otherwise it returns a request that needs to be sent out. + /// + /// [`cancel()`]: #method.cancel + pub fn cancel_with_code(&self, code: CancelCode) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.cancel(code); From d4e80883dcb18040a0725d16478ea1ff3b19763f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 25 Jun 2021 12:51:45 +0200 Subject: [PATCH 51/88] crypto: Propagate the we_started info to the SAS verification --- matrix_sdk_crypto/src/verification/machine.rs | 3 + .../src/verification/requests.rs | 58 +++++++++++-------- matrix_sdk_crypto/src/verification/sas/mod.rs | 17 ++++++ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index b5d1d60c..494f9b65 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -72,6 +72,7 @@ impl VerificationMachine { self.store.clone(), identity, None, + true, ); let request = match content { @@ -325,6 +326,7 @@ impl VerificationMachine { device, identity, false, + false, ) { Ok(sas) => { self.verifications.insert_sas(sas); @@ -461,6 +463,7 @@ mod test { bob_store, None, None, + true, ); machine diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 7eb621c6..2d26fd22 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -414,7 +414,7 @@ impl VerificationRequest { let inner = self.inner.lock().unwrap().clone(); if let InnerRequest::Ready(s) = inner { - s.receive_start(sender, content).await?; + s.receive_start(sender, content, self.we_started).await?; } else { warn!( sender = sender.as_str(), @@ -454,6 +454,7 @@ impl VerificationRequest { s.store.clone(), s.account.clone(), s.private_cross_signing_identity.clone(), + self.we_started, ) .await? { @@ -565,9 +566,10 @@ impl InnerRequest { content: &StartContent, other_device: ReadOnlyDevice, other_identity: Option, + we_started: bool, ) -> Result, OutgoingContent> { if let InnerRequest::Ready(s) = self { - Ok(Some(s.to_started_sas(content, other_device, other_identity)?)) + Ok(Some(s.to_started_sas(content, other_device, other_identity, we_started)?)) } else { Ok(None) } @@ -762,6 +764,7 @@ impl RequestState { content: &StartContent<'a>, other_device: ReadOnlyDevice, other_identity: Option, + we_started: bool, ) -> Result { Sas::from_start_event( (&*self.flow_id).to_owned(), @@ -772,6 +775,7 @@ impl RequestState { other_device, other_identity, true, + we_started, ) } @@ -868,6 +872,7 @@ impl RequestState { &self, sender: &UserId, content: &StartContent<'_>, + we_started: bool, ) -> Result<(), CryptoStoreError> { info!( sender = sender.as_str(), @@ -890,29 +895,31 @@ impl RequestState { let identity = self.store.get_user_identity(sender).await?; match content.method() { - StartMethod::SasV1(_) => match self.to_started_sas(content, device.clone(), identity) { - // TODO check if there is already a SAS verification, i.e. we - // already started one before the other side tried to do the - // same; ignore it if we did and we're the lexicographically - // smaller user ID, otherwise auto-accept the newly started one. - Ok(s) => { - info!("Started a new SAS verification."); - self.verification_cache.insert_sas(s); + StartMethod::SasV1(_) => { + match self.to_started_sas(content, device.clone(), identity, we_started) { + // TODO check if there is already a SAS verification, i.e. we + // already started one before the other side tried to do the + // same; ignore it if we did and we're the lexicographically + // smaller user ID, otherwise auto-accept the newly started one. + Ok(s) => { + info!("Started a new SAS verification."); + self.verification_cache.insert_sas(s); + } + Err(c) => { + warn!( + user_id = device.user_id().as_str(), + device_id = device.device_id().as_str(), + content =? c, + "Can't start key verification, canceling.", + ); + self.verification_cache.queue_up_content( + device.user_id(), + device.device_id(), + c, + ) + } } - Err(c) => { - warn!( - user_id = device.user_id().as_str(), - device_id = device.device_id().as_str(), - content =? c, - "Can't start key verification, canceling.", - ); - self.verification_cache.queue_up_content( - device.user_id(), - device.device_id(), - c, - ) - } - }, + } StartMethod::ReciprocateV1(_) => { if let Some(qr_verification) = self.verification_cache.get_qr(sender, content.flow_id()) @@ -941,6 +948,7 @@ impl RequestState { store: Arc, account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, + we_started: bool, ) -> Result, CryptoStoreError> { if !self.state.their_methods.contains(&VerificationMethod::SasV1) { return Ok(None); @@ -972,6 +980,7 @@ impl RequestState { store, other_identity, Some(t.to_owned()), + we_started, ); (sas, content) } @@ -984,6 +993,7 @@ impl RequestState { device, store, other_identity, + we_started, ); (sas, content) } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 471be4aa..1755cd03 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -55,6 +55,7 @@ pub struct Sas { account: ReadOnlyAccount, identities_being_verified: IdentitiesBeingVerified, flow_id: Arc, + we_started: bool, } impl Sas { @@ -114,6 +115,11 @@ impl Sas { self.inner.lock().unwrap().cancel_code() } + /// Did we initiate the verification flow. + pub fn we_started(&self) -> bool { + self.we_started + } + #[cfg(test)] #[allow(dead_code)] pub(crate) fn set_creation_time(&self, time: Instant) { @@ -127,6 +133,7 @@ impl Sas { other_device: ReadOnlyDevice, store: Arc, other_identity: Option, + we_started: bool, ) -> Sas { let flow_id = inner_sas.verification_flow_id(); @@ -142,6 +149,7 @@ impl Sas { account, identities_being_verified: identities, flow_id, + we_started, } } @@ -162,6 +170,7 @@ impl Sas { store: Arc, other_identity: Option, transaction_id: Option, + we_started: bool, ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start( account.clone(), @@ -178,6 +187,7 @@ impl Sas { other_device, store, other_identity, + we_started, ), content, ) @@ -193,6 +203,7 @@ impl Sas { /// /// Returns the new `Sas` object and a `StartEventContent` that needs to be /// sent out through the server to the other device. + #[allow(clippy::too_many_arguments)] pub(crate) fn start_in_room( flow_id: EventId, room_id: RoomId, @@ -201,6 +212,7 @@ impl Sas { other_device: ReadOnlyDevice, store: Arc, other_identity: Option, + we_started: bool, ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start_in_room( flow_id, @@ -218,6 +230,7 @@ impl Sas { other_device, store, other_identity, + we_started, ), content, ) @@ -243,6 +256,7 @@ impl Sas { other_device: ReadOnlyDevice, other_identity: Option, started_from_request: bool, + we_started: bool, ) -> Result { let inner = InnerSas::from_start_event( account.clone(), @@ -260,6 +274,7 @@ impl Sas { other_device, store, other_identity, + we_started, )) } @@ -568,6 +583,7 @@ mod test { alice_store, None, None, + true, ); let flow_id = alice.flow_id().to_owned(); @@ -582,6 +598,7 @@ mod test { alice_device, None, false, + false, ) .unwrap(); From af084528c8924c359bb42b7a359f288ccd9e8cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 25 Jun 2021 12:54:00 +0200 Subject: [PATCH 52/88] crypto: Remember who cancelled the verification This seems to be of limited use considering that people can just see the sender of the cancellation event or they perform the cancellation themselves using the cancel() method but EA seems to want this. --- matrix_sdk_crypto/src/verification/mod.rs | 5 +-- matrix_sdk_crypto/src/verification/qrcode.rs | 12 +++---- .../src/verification/requests.rs | 22 +++++++----- .../src/verification/sas/inner_sas.rs | 26 ++++++++++---- matrix_sdk_crypto/src/verification/sas/mod.rs | 7 +++- .../src/verification/sas/sas_state.rs | 34 +++++++++---------- 6 files changed, 64 insertions(+), 42 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index fd9850e2..b77d758b 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -167,12 +167,13 @@ impl Done { #[derive(Clone, Debug)] pub struct Cancelled { + cancelled_by_us: bool, cancel_code: CancelCode, reason: &'static str, } impl Cancelled { - fn new(code: CancelCode) -> Self { + fn new(cancelled_by_us: bool, code: CancelCode) -> Self { let reason = match code { CancelCode::Accepted => { "A m.key.verification.request was accepted by a different device." @@ -192,7 +193,7 @@ impl Cancelled { _ => "Unknown cancel reason", }; - Self { cancel_code: code, reason } + Self { cancelled_by_us, cancel_code: code, reason } } pub fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 33578544..e57e88fe 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -210,7 +210,7 @@ impl QrVerification { } fn cancel_with_code(&self, code: CancelCode) -> Option { - let new_state = QrState::::new(code); + let new_state = QrState::::new(true, code); let content = new_state.as_content(self.flow_id()); let mut state = self.state.lock().unwrap(); @@ -243,7 +243,7 @@ impl QrVerification { match self.identities.mark_as_done(Some(&devices), Some(&identities)).await? { VerificationResult::Ok => (None, None), VerificationResult::Cancel(c) => { - let canceled = QrState::::new(c); + let canceled = QrState::::new(false, c); let content = canceled.as_content(self.flow_id()); new_state = InnerState::Cancelled(canceled); (Some(content), None) @@ -594,8 +594,8 @@ impl QrState { } impl QrState { - fn new(cancel_code: CancelCode) -> Self { - QrState { state: Cancelled::new(cancel_code) } + fn new(cancelled_by_us: bool, cancel_code: CancelCode) -> Self { + QrState { state: Cancelled::new(cancelled_by_us, cancel_code) } } fn as_content(&self, flow_id: &FlowId) -> OutgoingContent { @@ -614,10 +614,10 @@ impl QrState { if self.state.secret == m.secret { Ok(QrState { state: Scanned {} }) } else { - Err(QrState::::new(CancelCode::KeyMismatch)) + Err(QrState::::new(false, CancelCode::KeyMismatch)) } } - _ => Err(QrState::::new(CancelCode::UnknownMethod)), + _ => Err(QrState::::new(false, CancelCode::UnknownMethod)), } } } diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 2d26fd22..14ed1f90 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -376,7 +376,7 @@ impl VerificationRequest { /// Cancel the verification request pub fn cancel(&self) -> Option { let mut inner = self.inner.lock().unwrap(); - inner.cancel(&CancelCode::User); + inner.cancel(true, &CancelCode::User); let content = if let InnerRequest::Cancelled(c) = &*inner { Some(c.state.as_content(self.flow_id())) @@ -436,7 +436,7 @@ impl VerificationRequest { pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) { if sender == self.other_user() { let mut inner = self.inner.lock().unwrap().clone(); - inner.cancel(content.cancel_code()); + inner.cancel(false, content.cancel_code()); } } @@ -539,12 +539,12 @@ impl InnerRequest { }) } - fn cancel(&mut self, cancel_code: &CancelCode) { + fn cancel(&mut self, cancelled_by_us: bool, cancel_code: &CancelCode) { *self = InnerRequest::Cancelled(match self { - InnerRequest::Created(s) => s.clone().into_canceled(cancel_code), - InnerRequest::Requested(s) => s.clone().into_canceled(cancel_code), - InnerRequest::Ready(s) => s.clone().into_canceled(cancel_code), - InnerRequest::Passive(s) => s.clone().into_canceled(cancel_code), + InnerRequest::Created(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), + InnerRequest::Requested(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), + InnerRequest::Ready(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), + InnerRequest::Passive(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), InnerRequest::Done(_) => return, InnerRequest::Cancelled(_) => return, }) @@ -604,7 +604,11 @@ impl RequestState { } } - fn into_canceled(self, cancel_code: &CancelCode) -> RequestState { + fn into_canceled( + self, + cancelled_by_us: bool, + cancel_code: &CancelCode, + ) -> RequestState { RequestState:: { account: self.account, private_cross_signing_identity: self.private_cross_signing_identity, @@ -612,7 +616,7 @@ impl RequestState { store: self.store, flow_id: self.flow_id, other_user_id: self.other_user_id, - state: Cancelled::new(cancel_code.clone()), + state: Cancelled::new(cancelled_by_us, cancel_code.clone()), } } } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 7ca37d5a..d8f35332 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -168,13 +168,17 @@ impl InnerSas { } } - pub fn cancel(self, code: CancelCode) -> (InnerSas, Option) { + pub fn cancel( + self, + cancelled_by_us: bool, + code: CancelCode, + ) -> (InnerSas, Option) { let sas = match self { - InnerSas::Created(s) => s.cancel(code), - InnerSas::Started(s) => s.cancel(code), - InnerSas::Accepted(s) => s.cancel(code), - InnerSas::KeyReceived(s) => s.cancel(code), - InnerSas::MacReceived(s) => s.cancel(code), + InnerSas::Created(s) => s.cancel(cancelled_by_us, code), + InnerSas::Started(s) => s.cancel(cancelled_by_us, code), + InnerSas::Accepted(s) => s.cancel(cancelled_by_us, code), + InnerSas::KeyReceived(s) => s.cancel(cancelled_by_us, code), + InnerSas::MacReceived(s) => s.cancel(cancelled_by_us, code), _ => return (self, None), }; @@ -230,7 +234,7 @@ impl InnerSas { } } AnyVerificationContent::Cancel(c) => { - let (sas, _) = self.cancel(c.cancel_code().to_owned()); + let (sas, _) = self.cancel(false, c.cancel_code().to_owned()); (sas, None) } AnyVerificationContent::Key(c) => match self { @@ -324,6 +328,14 @@ impl InnerSas { } } + pub fn cancelled_by_us(&self) -> Option { + if let InnerSas::Cancelled(c) = self { + Some(c.state.cancelled_by_us) + } else { + None + } + } + pub fn timed_out(&self) -> bool { match self { InnerSas::Created(s) => s.timed_out(), diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 1755cd03..6ef72715 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -115,6 +115,11 @@ impl Sas { self.inner.lock().unwrap().cancel_code() } + /// Has the verification flow been cancelled by us. + pub fn cancelled_by_us(&self) -> Option { + self.inner.lock().unwrap().cancelled_by_us() + } + /// Did we initiate the verification flow. pub fn we_started(&self) -> bool { self.we_started @@ -390,7 +395,7 @@ impl Sas { pub fn cancel_with_code(&self, code: CancelCode) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); - let (sas, content) = sas.cancel(code); + let (sas, content) = sas.cancel(true, code); *guard = sas; content.map(|c| match c { OutgoingContent::Room(room_id, content) => { diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 2ab1beb6..eb9d78c8 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -298,14 +298,14 @@ impl SasState { self.ids.other_device.clone() } - pub fn cancel(self, cancel_code: CancelCode) -> SasState { + pub fn cancel(self, cancelled_by_us: bool, cancel_code: CancelCode) -> SasState { SasState { inner: self.inner, ids: self.ids, creation_time: self.creation_time, last_event_time: self.last_event_time, verification_flow_id: self.verification_flow_id, - state: Arc::new(Cancelled::new(cancel_code)), + state: Arc::new(Cancelled::new(cancelled_by_us, cancel_code)), started_from_request: self.started_from_request, } } @@ -444,11 +444,11 @@ impl SasState { sender: &UserId, content: &AcceptContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; if let AcceptMethod::SasV1(content) = content.method() { - let accepted_protocols = - AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?; + let accepted_protocols = AcceptedProtocols::try_from(content.clone()) + .map_err(|c| self.clone().cancel(true, c))?; let start_content = self.as_content().into(); @@ -466,7 +466,7 @@ impl SasState { }), }) } else { - Err(self.cancel(CancelCode::UnknownMethod)) + Err(self.cancel(true, CancelCode::UnknownMethod)) } } } @@ -509,7 +509,7 @@ impl SasState { }, verification_flow_id: flow_id.clone(), - state: Arc::new(Cancelled::new(CancelCode::UnknownMethod)), + state: Arc::new(Cancelled::new(true, CancelCode::UnknownMethod)), }; if let StartMethod::SasV1(method_content) = content.method() { @@ -600,7 +600,7 @@ impl SasState { sender: &UserId, content: &KeyContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; let their_pubkey = content.public_key().to_owned(); @@ -640,7 +640,7 @@ impl SasState { sender: &UserId, content: &KeyContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; let commitment = calculate_commitment( content.public_key(), @@ -648,7 +648,7 @@ impl SasState { ); if self.state.commitment != commitment { - Err(self.cancel(CancelCode::InvalidMessage)) + Err(self.cancel(true, CancelCode::InvalidMessage)) } else { let their_pubkey = content.public_key().to_owned(); @@ -777,7 +777,7 @@ impl SasState { sender: &UserId, content: &MacContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), @@ -786,7 +786,7 @@ impl SasState { sender, content, ) - .map_err(|c| self.clone().cancel(c))?; + .map_err(|c| self.clone().cancel(true, c))?; Ok(SasState { inner: self.inner, @@ -837,7 +837,7 @@ impl SasState { sender: &UserId, content: &MacContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), @@ -846,7 +846,7 @@ impl SasState { sender, content, ) - .map_err(|c| self.clone().cancel(c))?; + .map_err(|c| self.clone().cancel(true, c))?; Ok(SasState { inner: self.inner, @@ -877,7 +877,7 @@ impl SasState { sender: &UserId, content: &MacContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; let (devices, master_keys) = receive_mac_event( &self.inner.lock().unwrap(), @@ -886,7 +886,7 @@ impl SasState { sender, content, ) - .map_err(|c| self.clone().cancel(c))?; + .map_err(|c| self.clone().cancel(true, c))?; Ok(SasState { inner: self.inner, @@ -1032,7 +1032,7 @@ impl SasState { sender: &UserId, content: &DoneContent, ) -> Result, SasState> { - self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(c))?; + self.check_event(sender, content.flow_id()).map_err(|c| self.clone().cancel(true, c))?; Ok(SasState { inner: self.inner, From 728d2988104914fb2d835f2117a46de247ddf8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 25 Jun 2021 12:56:30 +0200 Subject: [PATCH 53/88] crypto: Add a getter for the room id for the SAS verifications --- matrix_sdk_crypto/src/verification/sas/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 6ef72715..0079a4b0 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -89,6 +89,15 @@ impl Sas { &self.flow_id } + /// Get the room id if the verification is happening inside a room. + pub fn room_id(&self) -> Option<&RoomId> { + if let FlowId::InRoom(r, _) = self.flow_id() { + Some(r) + } else { + None + } + } + /// Does this verification flow support displaying emoji for the short /// authentication string. pub fn supports_emoji(&self) -> bool { From 80a30bcdd6de8972b7cefb9712c979cb6fb07418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 25 Jun 2021 17:55:39 +0200 Subject: [PATCH 54/88] crypto: Add all the common accessors to the qr code verification --- matrix_sdk_crypto/src/verification/qrcode.rs | 109 ++++++++++++++---- .../src/verification/requests.rs | 15 ++- 2 files changed, 97 insertions(+), 27 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index e57e88fe..0b414b80 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -33,7 +33,7 @@ use ruma::{ }, AnyMessageEventContent, AnyToDeviceEventContent, }, - DeviceIdBox, DeviceKeyAlgorithm, UserId, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, RoomId, UserId, }; use thiserror::Error; @@ -83,6 +83,7 @@ pub struct QrVerification { inner: Arc, state: Arc>, identities: IdentitiesBeingVerified, + we_started: bool, } impl std::fmt::Debug for QrVerification { @@ -115,6 +116,34 @@ impl QrVerification { self.identities.other_user_id() } + /// Get the device id of the other side. + pub fn other_device_id(&self) -> &DeviceId { + self.identities.other_device_id() + } + + /// Did we initiate the verification request + pub fn we_started(&self) -> bool { + self.we_started + } + + /// Get the `CancelCode` that cancelled this verification request. + pub fn cancel_code(&self) -> Option { + if let InnerState::Cancelled(c) = &*self.state.lock().unwrap() { + Some(c.state.cancel_code.to_owned()) + } else { + None + } + } + + /// Has the verification flow been cancelled by us. + pub fn cancelled_by_us(&self) -> Option { + if let InnerState::Cancelled(c) = &*self.state.lock().unwrap() { + Some(c.state.cancelled_by_us) + } else { + None + } + } + /// Has the verification flow completed. pub fn is_done(&self) -> bool { matches!(&*self.state.lock().unwrap(), InnerState::Done(_)) @@ -135,6 +164,14 @@ impl QrVerification { &self.flow_id } + /// Get the room id if the verification is happening inside a room. + pub fn room_id(&self) -> Option<&RoomId> { + match self.flow_id() { + FlowId::ToDevice(_) => None, + FlowId::InRoom(r, _) => Some(r), + } + } + /// Generate a QR code object that is representing this verification flow. /// /// The `QrCode` can then be rendered as an image or as an unicode string. @@ -159,6 +196,38 @@ impl QrVerification { self.cancel_with_code(CancelCode::User).map(|c| self.content_to_request(c)) } + /// Cancel the verification. + /// + /// This cancels the verification with given `CancelCode`. + /// + /// **Note**: This method should generally not be used, the [`cancel()`] + /// method should be preferred. The SDK will automatically cancel with the + /// approprate cancel code, user initiated cancellations should only cancel + /// with the `CancelCode::User` + /// + /// Returns None if the `Sas` object is already in a canceled state, + /// otherwise it returns a request that needs to be sent out. + /// + /// [`cancel()`]: #method.cancel + pub fn cancel_with_code(&self, code: CancelCode) -> Option { + let new_state = QrState::::new(true, code); + let content = new_state.as_content(self.flow_id()); + + let mut state = self.state.lock().unwrap(); + + match &*state { + InnerState::Confirmed(_) + | InnerState::Created(_) + | InnerState::Scanned(_) + | InnerState::Reciprocated(_) + | InnerState::Done(_) => { + *state = InnerState::Cancelled(new_state); + Some(content) + } + InnerState::Cancelled(_) => None, + } + } + /// Notify the other side that we have successfully scanned the QR code and /// that the QR verification flow can start. /// @@ -209,25 +278,6 @@ impl QrVerification { } } - fn cancel_with_code(&self, code: CancelCode) -> Option { - let new_state = QrState::::new(true, code); - let content = new_state.as_content(self.flow_id()); - - let mut state = self.state.lock().unwrap(); - - match &*state { - InnerState::Confirmed(_) - | InnerState::Created(_) - | InnerState::Scanned(_) - | InnerState::Reciprocated(_) - | InnerState::Done(_) => { - *state = InnerState::Cancelled(new_state); - Some(content) - } - InnerState::Cancelled(_) => None, - } - } - async fn mark_as_done( &self, new_state: QrState, @@ -351,6 +401,7 @@ impl QrVerification { own_master_key: String, other_device_key: String, identities: IdentitiesBeingVerified, + we_started: bool, ) -> Self { let secret = Self::generate_secret(); @@ -362,7 +413,7 @@ impl QrVerification { ) .into(); - Self::new_helper(store, flow_id, inner, identities) + Self::new_helper(store, flow_id, inner, identities, we_started) } pub(crate) fn new_self_no_master( @@ -371,6 +422,7 @@ impl QrVerification { flow_id: FlowId, own_master_key: String, identities: IdentitiesBeingVerified, + we_started: bool, ) -> QrVerification { let secret = Self::generate_secret(); @@ -382,7 +434,7 @@ impl QrVerification { ) .into(); - Self::new_helper(store, flow_id, inner, identities) + Self::new_helper(store, flow_id, inner, identities, we_started) } pub(crate) fn new_cross( @@ -391,6 +443,7 @@ impl QrVerification { own_master_key: String, other_master_key: String, identities: IdentitiesBeingVerified, + we_started: bool, ) -> Self { let secret = Self::generate_secret(); @@ -403,9 +456,10 @@ impl QrVerification { let inner: QrVerificationData = VerificationData::new(event_id, own_master_key, other_master_key, secret).into(); - Self::new_helper(store, flow_id, inner, identities) + Self::new_helper(store, flow_id, inner, identities, we_started) } + #[allow(clippy::too_many_arguments)] pub(crate) async fn from_scan( store: Arc, own_account: ReadOnlyAccount, @@ -414,6 +468,7 @@ impl QrVerification { other_device_id: DeviceIdBox, flow_id: FlowId, qr_code: QrVerificationData, + we_started: bool, ) -> Result { if flow_id.as_str() != qr_code.flow_id() { return Err(ScanError::FlowIdMismatch { @@ -510,6 +565,7 @@ impl QrVerification { })) .into(), identities, + we_started, }) } @@ -518,6 +574,7 @@ impl QrVerification { flow_id: FlowId, inner: QrVerificationData, identities: IdentitiesBeingVerified, + we_started: bool, ) -> Self { let secret = inner.secret().to_owned(); @@ -527,6 +584,7 @@ impl QrVerification { inner: inner.into(), state: Mutex::new(InnerState::Created(QrState { state: Created { secret } })).into(), identities, + we_started, } } } @@ -747,6 +805,7 @@ mod test { flow_id.clone(), master_key.clone(), identities.clone(), + false, ); assert_eq!(verification.inner.first_key(), &device_key); @@ -758,6 +817,7 @@ mod test { master_key.clone(), device_key.clone(), identities.clone(), + false, ); assert_eq!(verification.inner.first_key(), &master_key); @@ -775,6 +835,7 @@ mod test { master_key.clone(), bob_master_key.clone(), identities, + false, ); assert_eq!(verification.inner.first_key(), &master_key); @@ -818,6 +879,7 @@ mod test { flow_id.clone(), master_key.clone(), identities, + false, ); let bob_store = memory_store(); @@ -838,6 +900,7 @@ mod test { alice_account.device_id().to_owned(), flow_id, qr_code, + false, ) .await .unwrap(); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 14ed1f90..e858ceca 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -277,7 +277,7 @@ impl VerificationRequest { /// Generate a QR code that can be used by another client to start a QR code /// based verification. pub async fn generate_qr_code(&self) -> Result, CryptoStoreError> { - self.inner.lock().unwrap().generate_qr_code().await + self.inner.lock().unwrap().generate_qr_code(self.we_started).await } /// Start a QR code verification by providing a scanned QR code for this @@ -303,6 +303,7 @@ impl VerificationRequest { r.state.other_device_id.clone(), r.flow_id.as_ref().to_owned(), data, + self.we_started, ) .await?, )) @@ -550,11 +551,11 @@ impl InnerRequest { }) } - async fn generate_qr_code(&self) -> Result, CryptoStoreError> { + async fn generate_qr_code(&self, we_started: bool) -> Result, CryptoStoreError> { match self { InnerRequest::Created(_) => Ok(None), InnerRequest::Requested(_) => Ok(None), - InnerRequest::Ready(s) => s.generate_qr_code().await, + InnerRequest::Ready(s) => s.generate_qr_code(we_started).await, InnerRequest::Passive(_) => Ok(None), InnerRequest::Done(_) => Ok(None), InnerRequest::Cancelled(_) => Ok(None), @@ -783,7 +784,10 @@ impl RequestState { ) } - async fn generate_qr_code(&self) -> Result, CryptoStoreError> { + async fn generate_qr_code( + &self, + we_started: bool, + ) -> Result, CryptoStoreError> { // TODO return an error explaining why we can't generate a QR code? // If we didn't state that we support showing QR codes or if the other @@ -829,6 +833,7 @@ impl RequestState { .unwrap() .to_owned(), identites, + we_started, )) } else { Some(QrVerification::new_self_no_master( @@ -837,6 +842,7 @@ impl RequestState { self.flow_id.as_ref().to_owned(), i.master_key().get_first_key().unwrap().to_owned(), identites, + we_started, )) } } @@ -852,6 +858,7 @@ impl RequestState { .to_owned(), i.master_key().get_first_key().unwrap().to_owned(), identites, + we_started, )), } } else { From 100a936f1bfe2d404dc35e19cb7920dab90733aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 28 Jun 2021 09:53:56 +0200 Subject: [PATCH 55/88] crypto: Add a method to check if the scanning has been confirmed --- matrix_sdk_crypto/src/verification/qrcode.rs | 7 ++++++- matrix_sdk_crypto/src/verification/requests.rs | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 0b414b80..b1e7adfd 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -101,10 +101,15 @@ impl QrVerification { /// /// When the verification object is in this state it's required that the /// user confirms that the other side has scanned the QR code. - pub fn is_scanned(&self) -> bool { + pub fn has_been_scanned(&self) -> bool { matches!(&*self.state.lock().unwrap(), InnerState::Scanned(_)) } + /// Has the scanning of the QR code been confirmed by us. + pub fn has_been_confirmed(&self) -> bool { + matches!(&*self.state.lock().unwrap(), InnerState::Confirmed(_)) + } + /// Get our own user id. pub fn user_id(&self) -> &UserId { self.identities.user_id() diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index e858ceca..ac3cbf8e 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -551,7 +551,10 @@ impl InnerRequest { }) } - async fn generate_qr_code(&self, we_started: bool) -> Result, CryptoStoreError> { + async fn generate_qr_code( + &self, + we_started: bool, + ) -> Result, CryptoStoreError> { match self { InnerRequest::Created(_) => Ok(None), InnerRequest::Requested(_) => Ok(None), From 63659c960476c28acece2e7b0e5d5767eac84355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 28 Jun 2021 15:27:01 +0200 Subject: [PATCH 56/88] crypto: Fix verification requests getting cancelled --- matrix_sdk_crypto/src/verification/requests.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index ac3cbf8e..1db22561 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -436,7 +436,12 @@ impl VerificationRequest { pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) { if sender == self.other_user() { - let mut inner = self.inner.lock().unwrap().clone(); + trace!( + sender = sender.as_str(), + code = content.cancel_code().as_str(), + "Cancelling a verification request, other user has cancelled" + ); + let mut inner = self.inner.lock().unwrap(); inner.cancel(false, content.cancel_code()); } } @@ -548,7 +553,7 @@ impl InnerRequest { InnerRequest::Passive(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), InnerRequest::Done(_) => return, InnerRequest::Cancelled(_) => return, - }) + }); } async fn generate_qr_code( From ee6b804804215276f35c56afc34093ef2a134355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 28 Jun 2021 15:28:30 +0200 Subject: [PATCH 57/88] crypto: Allow QR code verifications to get into the cancelled state as well. --- matrix_sdk_crypto/src/verification/machine.rs | 13 ++++++-- matrix_sdk_crypto/src/verification/qrcode.rs | 31 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 494f9b65..a2a4abab 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -287,9 +287,16 @@ impl VerificationMachine { verification.receive_cancel(event.sender(), c); } - if let Some(sas) = self.get_sas(event.sender(), flow_id.as_str()) { - // This won't produce an outgoing content - let _ = sas.receive_any_event(event.sender(), &content); + if let Some(verification) = + self.get_verification(event.sender(), flow_id.as_str()) + { + match verification { + Verification::SasV1(sas) => { + // This won't produce an outgoing content + let _ = sas.receive_any_event(event.sender(), &content); + } + Verification::QrV1(qr) => qr.receive_cancel(event.sender(), c), + } } } AnyVerificationContent::Ready(c) => { diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index b1e7adfd..0dd4b576 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -36,9 +36,10 @@ use ruma::{ DeviceId, DeviceIdBox, DeviceKeyAlgorithm, RoomId, UserId, }; use thiserror::Error; +use tracing::trace; use super::{ - event_enums::{DoneContent, OutgoingContent, OwnedStartContent, StartContent}, + event_enums::{CancelContent, DoneContent, OutgoingContent, OwnedStartContent, StartContent}, Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ @@ -393,6 +394,28 @@ impl QrVerification { } } + pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) { + if sender == self.other_user_id() { + let mut state = self.state.lock().unwrap(); + + let new_state = match &*state { + InnerState::Created(s) => s.clone().into_cancelled(content), + InnerState::Scanned(s) => s.clone().into_cancelled(content), + InnerState::Confirmed(s) => s.clone().into_cancelled(content), + InnerState::Reciprocated(s) => s.clone().into_cancelled(content), + InnerState::Done(_) | InnerState::Cancelled(_) => return, + }; + + trace!( + sender = sender.as_str(), + code = content.cancel_code().as_str(), + "Cancelling a QR verification, other user has cancelled" + ); + + *state = InnerState::Cancelled(new_state); + } + } + fn generate_secret() -> String { let mut shared_secret = [0u8; SECRET_SIZE]; getrandom::getrandom(&mut shared_secret) @@ -609,6 +632,12 @@ struct QrState { state: S, } +impl QrState { + pub fn into_cancelled(self, content: &CancelContent<'_>) -> QrState { + QrState { state: Cancelled::new(false, content.cancel_code().to_owned()) } + } +} + #[derive(Clone, Debug)] struct Created { secret: String, From 113587247eed9a20aec2835c1faed97767ccbb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 28 Jun 2021 16:30:41 +0200 Subject: [PATCH 58/88] crypto: Notify our users that a verification timed out on our end --- matrix_sdk_crypto/src/machine.rs | 4 +-- .../src/verification/event_enums.rs | 6 ++--- matrix_sdk_crypto/src/verification/machine.rs | 26 ++++++++++++++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 75b1fd40..68b4426b 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -789,7 +789,7 @@ impl OlmMachine { one_time_keys_counts: &BTreeMap, ) -> OlmResult { // Remove verification objects that have expired or are done. - self.verification_machine.garbage_collect(); + let mut events = self.verification_machine.garbage_collect(); // Always save the account, a new session might get created which also // touches the account. @@ -804,8 +804,6 @@ impl OlmMachine { } } - let mut events = Vec::new(); - for mut raw_event in to_device_events.events { let event = match raw_event.deserialize() { Ok(e) => e, diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index 39b9d195..9c63f540 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -666,7 +666,8 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent { } #[cfg(test)] -use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest}; +use crate::OutgoingVerificationRequest; +use crate::{OutgoingRequest, RoomMessageRequest, ToDeviceRequest}; #[cfg(test)] impl From for OutgoingContent { @@ -678,14 +679,12 @@ impl From for OutgoingContent { } } -#[cfg(test)] impl From for OutgoingContent { fn from(value: RoomMessageRequest) -> Self { (value.room_id, value.content).into() } } -#[cfg(test)] impl TryFrom for OutgoingContent { type Error = String; @@ -736,7 +735,6 @@ impl TryFrom for OutgoingContent { } } -#[cfg(test)] impl TryFrom for OutgoingContent { type Error = String; diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index a2a4abab..330f02cc 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -12,11 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{convert::TryFrom, sync::Arc}; +use std::{ + convert::{TryFrom, TryInto}, + sync::Arc, +}; use dashmap::DashMap; use matrix_sdk_common::{locks::Mutex, uuid::Uuid}; -use ruma::{DeviceId, MilliSecondsSinceUnixEpoch, UserId}; +use ruma::{ + events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent}, + serde::Raw, + DeviceId, MilliSecondsSinceUnixEpoch, UserId, +}; use tracing::{info, trace, warn}; use super::{ @@ -165,15 +172,28 @@ impl VerificationMachine { self.verifications.outgoing_requests() } - pub fn garbage_collect(&self) { + pub fn garbage_collect(&self) -> Vec> { + let mut events = vec![]; + for user_verification in self.requests.iter() { user_verification.retain(|_, v| !(v.is_done() || v.is_cancelled())); } self.requests.retain(|_, v| !v.is_empty()); for request in self.verifications.garbage_collect() { + if let Ok(OutgoingContent::ToDevice(AnyToDeviceEventContent::KeyVerificationCancel( + content, + ))) = request.clone().try_into() + { + let event = ToDeviceEvent { content, sender: self.account.user_id().to_owned() }; + + events.push(AnyToDeviceEvent::KeyVerificationCancel(event).into()); + } + self.verifications.add_request(request) } + + events } async fn mark_sas_as_done( From 9052843acbc762388f4ae59e6dad0d580914cf59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Tue, 29 Jun 2021 09:16:22 +0200 Subject: [PATCH 59/88] crypto: Add another SAS state so we know when both parties accepted --- .../src/verification/sas/inner_sas.rs | 33 ++++++++++++++-- matrix_sdk_crypto/src/verification/sas/mod.rs | 39 +++++++++++++------ .../src/verification/sas/sas_state.rs | 34 ++++++++++++++-- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index d8f35332..fd6fe7a1 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -24,6 +24,7 @@ use ruma::{ use super::{ sas_state::{ Accepted, Confirmed, Created, KeyReceived, MacReceived, SasState, Started, WaitingForDone, + WeAccepted, }, FlowId, }; @@ -41,6 +42,7 @@ pub enum InnerSas { Created(SasState), Started(SasState), Accepted(SasState), + WeAccepted(SasState), KeyReceived(SasState), Confirmed(SasState), MacReceived(SasState), @@ -65,6 +67,7 @@ impl InnerSas { match self { InnerSas::Created(s) => s.started_from_request, InnerSas::Started(s) => s.started_from_request, + InnerSas::WeAccepted(s) => s.started_from_request, InnerSas::Accepted(s) => s.started_from_request, InnerSas::KeyReceived(s) => s.started_from_request, InnerSas::Confirmed(s) => s.started_from_request, @@ -75,6 +78,19 @@ impl InnerSas { } } + pub fn has_been_accepted(&self) -> bool { + match self { + InnerSas::Created(_) | InnerSas::Started(_) | InnerSas::Cancelled(_) => false, + InnerSas::Accepted(_) + | InnerSas::WeAccepted(_) + | InnerSas::KeyReceived(_) + | InnerSas::Confirmed(_) + | InnerSas::MacReceived(_) + | InnerSas::WaitingForDone(_) + | InnerSas::Done(_) => true, + } + } + pub fn supports_emoji(&self) -> bool { match self { InnerSas::Created(_) => false, @@ -83,6 +99,11 @@ impl InnerSas { .accepted_protocols .short_auth_string .contains(&ShortAuthenticationString::Emoji), + InnerSas::WeAccepted(s) => s + .state + .accepted_protocols + .short_auth_string + .contains(&ShortAuthenticationString::Emoji), InnerSas::Accepted(s) => s .state .accepted_protocols @@ -144,9 +165,11 @@ impl InnerSas { } } - pub fn accept(&self) -> Option { + pub fn accept(self) -> Option<(InnerSas, OwnedAcceptContent)> { if let InnerSas::Started(s) = self { - Some(s.as_content()) + let sas = s.into_accepted(); + let content = sas.as_content(); + Some((InnerSas::WeAccepted(sas), content)) } else { None } @@ -165,6 +188,7 @@ impl InnerSas { InnerSas::MacReceived(s) => s.set_creation_time(time), InnerSas::Done(s) => s.set_creation_time(time), InnerSas::WaitingForDone(s) => s.set_creation_time(time), + InnerSas::WeAccepted(s) => s.set_creation_time(time), } } @@ -177,6 +201,7 @@ impl InnerSas { InnerSas::Created(s) => s.cancel(cancelled_by_us, code), InnerSas::Started(s) => s.cancel(cancelled_by_us, code), InnerSas::Accepted(s) => s.cancel(cancelled_by_us, code), + InnerSas::WeAccepted(s) => s.cancel(cancelled_by_us, code), InnerSas::KeyReceived(s) => s.cancel(cancelled_by_us, code), InnerSas::MacReceived(s) => s.cancel(cancelled_by_us, code), _ => return (self, None), @@ -245,7 +270,7 @@ impl InnerSas { (InnerSas::Cancelled(s), Some(content)) } }, - InnerSas::Started(s) => match s.into_key_received(sender, c) { + InnerSas::WeAccepted(s) => match s.into_key_received(sender, c) { Ok(s) => { let content = s.as_content(); (InnerSas::KeyReceived(s), Some(content)) @@ -347,6 +372,7 @@ impl InnerSas { InnerSas::MacReceived(s) => s.timed_out(), InnerSas::WaitingForDone(s) => s.timed_out(), InnerSas::Done(s) => s.timed_out(), + InnerSas::WeAccepted(s) => s.timed_out(), } } @@ -361,6 +387,7 @@ impl InnerSas { InnerSas::MacReceived(s) => s.verification_flow_id.clone(), InnerSas::WaitingForDone(s) => s.verification_flow_id.clone(), InnerSas::Done(s) => s.verification_flow_id.clone(), + InnerSas::WeAccepted(s) => s.verification_flow_id.clone(), } } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 0079a4b0..eb4b78af 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -119,6 +119,11 @@ impl Sas { self.inner.lock().unwrap().have_we_confirmed() } + /// Has the verification been accepted by both parties. + pub fn has_been_accepted(&self) -> bool { + self.inner.lock().unwrap().has_been_accepted() + } + /// Get the cancel code of this SAS verification if it has been cancelled pub fn cancel_code(&self) -> Option { self.inner.lock().unwrap().cancel_code() @@ -310,18 +315,28 @@ impl Sas { &self, settings: AcceptSettings, ) -> Option { - self.inner.lock().unwrap().accept().map(|c| match settings.apply(c) { - OwnedAcceptContent::ToDevice(c) => { - let content = AnyToDeviceEventContent::KeyVerificationAccept(c); - self.content_to_request(content).into() - } - OwnedAcceptContent::Room(room_id, content) => RoomMessageRequest { - room_id, - txn_id: Uuid::new_v4(), - content: AnyMessageEventContent::KeyVerificationAccept(content), - } - .into(), - }) + let mut guard = self.inner.lock().unwrap(); + let sas: InnerSas = (*guard).clone(); + + if let Some((sas, content)) = sas.accept() { + *guard = sas; + let content = settings.apply(content); + + Some(match content { + OwnedAcceptContent::ToDevice(c) => { + let content = AnyToDeviceEventContent::KeyVerificationAccept(c); + self.content_to_request(content).into() + } + OwnedAcceptContent::Room(room_id, content) => RoomMessageRequest { + room_id, + txn_id: Uuid::new_v4(), + content: AnyMessageEventContent::KeyVerificationAccept(content), + } + .into(), + }) + } else { + None + } } /// Confirm the Sas verification. diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index eb9d78c8..af5dad17 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -241,6 +241,15 @@ pub struct Accepted { commitment: String, } +/// The SAS state we're going to be in after we accepted our +/// verification start event. +#[derive(Clone, Debug)] +pub struct WeAccepted { + we_started: bool, + pub accepted_protocols: Arc, + commitment: String, +} + /// The SAS state we're going to be in after we received the public key of the /// other participant. /// @@ -548,6 +557,24 @@ impl SasState { } } + pub fn into_accepted(self) -> SasState { + SasState { + inner: self.inner, + ids: self.ids, + verification_flow_id: self.verification_flow_id, + creation_time: self.creation_time, + last_event_time: self.last_event_time, + started_from_request: self.started_from_request, + state: Arc::new(WeAccepted { + we_started: false, + accepted_protocols: self.state.accepted_protocols.clone(), + commitment: self.state.commitment.clone(), + }), + } + } +} + +impl SasState { /// Get the content for the accept event. /// /// The content needs to be sent to the other device. @@ -1093,7 +1120,7 @@ mod test { }; use serde_json::json; - use super::{Accepted, Created, SasState, Started}; + use super::{Accepted, Created, SasState, Started, WeAccepted}; use crate::{ verification::event_enums::{AcceptContent, KeyContent, MacContent, StartContent}, ReadOnlyAccount, ReadOnlyDevice, @@ -1115,7 +1142,7 @@ mod test { "BOBDEVCIE".into() } - async fn get_sas_pair() -> (SasState, SasState) { + async fn get_sas_pair() -> (SasState, SasState) { let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); let alice_device = ReadOnlyDevice::from_account(&alice).await; @@ -1135,8 +1162,9 @@ mod test { &start_content.as_start_content(), false, ); + let bob_sas = bob_sas.unwrap().into_accepted(); - (alice_sas, bob_sas.unwrap()) + (alice_sas, bob_sas) } #[tokio::test] From db0843a47abd794f272a0d9fc220402d8e2264e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 1 Jul 2021 10:27:45 +0200 Subject: [PATCH 60/88] crypto: Don't panic if we don't have the keys to generate a QR code --- .../src/verification/requests.rs | 114 ++++++++++++------ 1 file changed, 77 insertions(+), 37 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 1db22561..e84193e1 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -796,8 +796,6 @@ impl RequestState { &self, we_started: bool, ) -> Result, CryptoStoreError> { - // TODO return an error explaining why we can't generate a QR code? - // If we didn't state that we support showing QR codes or if the other // side doesn't support scanning QR codes bail early. if !self.state.our_methods.contains(&VerificationMethod::QrCodeShowV1) @@ -830,44 +828,86 @@ impl RequestState { let verification = if let Some(identity) = &identites.identity_being_verified { match &identity { UserIdentities::Own(i) => { - if identites.can_sign_devices().await { - Some(QrVerification::new_self( - self.store.clone(), - self.flow_id.as_ref().to_owned(), - i.master_key().get_first_key().unwrap().to_owned(), - identites - .other_device() - .get_key(DeviceKeyAlgorithm::Ed25519) - .unwrap() - .to_owned(), - identites, - we_started, - )) + if let Some(master_key) = i.master_key().get_first_key() { + if identites.can_sign_devices().await { + if let Some(device_key) = + identites.other_device().get_key(DeviceKeyAlgorithm::Ed25519) + { + Some(QrVerification::new_self( + self.store.clone(), + self.flow_id.as_ref().to_owned(), + master_key.to_owned(), + device_key.to_owned(), + identites, + we_started, + )) + } else { + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't create a QR code, the other device \ + doesn't have a valid device key" + ); + None + } + } else { + Some(QrVerification::new_self_no_master( + self.account.clone(), + self.store.clone(), + self.flow_id.as_ref().to_owned(), + master_key.to_owned(), + identites, + we_started, + )) + } } else { - Some(QrVerification::new_self_no_master( - self.account.clone(), - self.store.clone(), - self.flow_id.as_ref().to_owned(), - i.master_key().get_first_key().unwrap().to_owned(), - identites, - we_started, - )) + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't create a QR code, our cross signing identity \ + doesn't contain a valid master key" + ); + None + } + } + UserIdentities::Other(i) => { + if let Some(other_master) = i.master_key().get_first_key() { + // TODO we can get the master key from the public + // identity if we don't have the private one and we + // trust the public one. + if let Some(own_master) = self + .private_cross_signing_identity + .master_public_key() + .await + .and_then(|m| m.get_first_key().map(|m| m.to_owned())) + { + Some(QrVerification::new_cross( + self.store.clone(), + self.flow_id.as_ref().to_owned(), + own_master, + other_master.to_owned(), + identites, + we_started, + )) + } else { + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't create a QR code, we don't trust our own \ + master key" + ); + None + } + } else { + warn!( + user_id = self.other_user_id.as_str(), + device_id = self.state.other_device_id.as_str(), + "Can't create a QR code, the user's identity \ + doesn't have a valid master key" + ); + None } } - UserIdentities::Other(i) => Some(QrVerification::new_cross( - self.store.clone(), - self.flow_id.as_ref().to_owned(), - self.private_cross_signing_identity - .master_public_key() - .await - .unwrap() - .get_first_key() - .unwrap() - .to_owned(), - i.master_key().get_first_key().unwrap().to_owned(), - identites, - we_started, - )), } } else { warn!( From 69225ad00b162e00017cafcd27b0d65de8ae1919 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Fri, 2 Jul 2021 18:05:34 +0200 Subject: [PATCH 61/88] Use rustdoc-map to link to external crates in master docs --- .cargo/config.toml | 2 ++ .github/workflows/docs.yml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..b2c9bde3 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[doc.extern-map.registries] +crates-io = "https://docs.rs/" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index feabc582..6a8d9a7a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -27,7 +27,7 @@ jobs: RUSTDOCFLAGS: "--enable-index-page -Zunstable-options" with: command: doc - args: --no-deps --workspace --features docs + args: --no-deps --workspace --features docs -Zrustdoc-map - name: Deploy docs if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} From 27ad1f42da2f0d7c9c9c0778f17b7bd89d56ae9d Mon Sep 17 00:00:00 2001 From: Sorunome Date: Sat, 3 Jul 2021 12:00:13 +0200 Subject: [PATCH 62/88] use hoist_and_deserialize for state events --- matrix_sdk/src/event_handler/mod.rs | 9 +++++---- matrix_sdk_base/src/client.rs | 2 +- matrix_sdk_base/src/lib.rs | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/matrix_sdk/src/event_handler/mod.rs b/matrix_sdk/src/event_handler/mod.rs index 5b21b0a7..e7cf2105 100644 --- a/matrix_sdk/src/event_handler/mod.rs +++ b/matrix_sdk/src/event_handler/mod.rs @@ -14,6 +14,7 @@ // limitations under the License. use std::ops::Deref; +use matrix_sdk_base::{hoist_and_deserialize_state_event, hoist_room_event_prev_content}; use matrix_sdk_common::async_trait; use ruma::{ api::client::r0::push::get_notifications::Notification, @@ -91,7 +92,7 @@ impl Handler { } for (raw_event, event) in room_info.state.events.iter().filter_map(|e| { - if let Ok(d) = e.deserialize() { + if let Ok(d) = hoist_and_deserialize_state_event(e) { Some((e, d)) } else { None @@ -101,7 +102,7 @@ impl Handler { } for (raw_event, event) in room_info.timeline.events.iter().filter_map(|e| { - if let Ok(d) = e.event.deserialize() { + if let Ok(d) = hoist_room_event_prev_content(&e.event) { Some((&e.event, d)) } else { None @@ -121,7 +122,7 @@ impl Handler { } for (raw_event, event) in room_info.state.events.iter().filter_map(|e| { - if let Ok(d) = e.deserialize() { + if let Ok(d) = hoist_and_deserialize_state_event(e) { Some((e, d)) } else { None @@ -131,7 +132,7 @@ impl Handler { } for (raw_event, event) in room_info.timeline.events.iter().filter_map(|e| { - if let Ok(d) = e.event.deserialize() { + if let Ok(d) = hoist_room_event_prev_content(&e.event) { Some((&e.event, d)) } else { None diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index b36d9041..7c7497ea 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -129,7 +129,7 @@ fn hoist_member_event( Ok(e) } -fn hoist_room_event_prev_content( +pub fn hoist_room_event_prev_content( event: &Raw, ) -> StdResult { let prev_content = event diff --git a/matrix_sdk_base/src/lib.rs b/matrix_sdk_base/src/lib.rs index 326700ce..051f2181 100644 --- a/matrix_sdk_base/src/lib.rs +++ b/matrix_sdk_base/src/lib.rs @@ -50,7 +50,9 @@ mod rooms; mod session; mod store; -pub use client::{BaseClient, BaseClientConfig}; +pub use client::{ + hoist_and_deserialize_state_event, hoist_room_event_prev_content, BaseClient, BaseClientConfig, +}; #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] pub use matrix_sdk_crypto as crypto; From c5df7c5356c2540ede95f41e3565e91ca7de5777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 8 Jul 2021 12:30:30 +0200 Subject: [PATCH 63/88] crypto: Add methods to request verification from users --- matrix_sdk_crypto/src/identities/device.rs | 24 +- matrix_sdk_crypto/src/identities/manager.rs | 16 +- matrix_sdk_crypto/src/identities/mod.rs | 4 +- matrix_sdk_crypto/src/identities/user.rs | 239 +++++++++++++++--- matrix_sdk_crypto/src/lib.rs | 3 +- matrix_sdk_crypto/src/machine.rs | 14 +- matrix_sdk_crypto/src/olm/signing/mod.rs | 14 +- .../src/olm/signing/pk_signing.rs | 4 +- matrix_sdk_crypto/src/store/memorystore.rs | 6 +- matrix_sdk_crypto/src/store/mod.rs | 29 ++- matrix_sdk_crypto/src/store/sled.rs | 4 +- matrix_sdk_crypto/src/verification/machine.rs | 75 +++++- matrix_sdk_crypto/src/verification/mod.rs | 14 +- matrix_sdk_crypto/src/verification/qrcode.rs | 12 +- .../src/verification/requests.rs | 171 ++++++------- .../src/verification/sas/helpers.rs | 6 +- .../src/verification/sas/inner_sas.rs | 14 +- matrix_sdk_crypto/src/verification/sas/mod.rs | 12 +- .../src/verification/sas/sas_state.rs | 16 +- 19 files changed, 455 insertions(+), 222 deletions(-) diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index d9f6e8cc..79884c84 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -39,11 +39,11 @@ use tracing::warn; use super::{atomic_bool_deserializer, atomic_bool_serializer}; use crate::{ error::{EventError, OlmError, OlmResult, SignatureError}, - identities::{OwnUserIdentity, UserIdentities}, + identities::{ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities}, olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session, Utility}, store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult}, verification::VerificationMachine, - OutgoingVerificationRequest, Sas, ToDeviceRequest, + OutgoingVerificationRequest, Sas, ToDeviceRequest, VerificationRequest, }; #[cfg(test)] use crate::{OlmMachine, ReadOnlyAccount}; @@ -104,8 +104,8 @@ pub struct Device { pub(crate) inner: ReadOnlyDevice, pub(crate) private_identity: Arc>, pub(crate) verification_machine: VerificationMachine, - pub(crate) own_identity: Option, - pub(crate) device_owner_identity: Option, + pub(crate) own_identity: Option, + pub(crate) device_owner_identity: Option, } impl std::fmt::Debug for Device { @@ -228,8 +228,8 @@ pub struct UserDevices { pub(crate) inner: HashMap, pub(crate) private_identity: Arc>, pub(crate) verification_machine: VerificationMachine, - pub(crate) own_identity: Option, - pub(crate) device_owner_identity: Option, + pub(crate) own_identity: Option, + pub(crate) device_owner_identity: Option, } impl UserDevices { @@ -382,16 +382,16 @@ impl ReadOnlyDevice { pub(crate) fn verified( &self, - own_identity: &Option, - device_owner: &Option, + own_identity: &Option, + device_owner: &Option, ) -> bool { self.is_locally_trusted() || self.is_cross_signing_trusted(own_identity, device_owner) } pub(crate) fn is_cross_signing_trusted( &self, - own_identity: &Option, - device_owner: &Option, + own_identity: &Option, + device_owner: &Option, ) -> bool { own_identity.as_ref().map_or(false, |own_identity| { // Our own identity needs to be marked as verified. @@ -401,14 +401,14 @@ impl ReadOnlyDevice { .map(|device_identity| match device_identity { // If it's one of our own devices, just check that // we signed the device. - UserIdentities::Own(_) => { + ReadOnlyUserIdentities::Own(_) => { own_identity.is_device_signed(self).map_or(false, |_| true) } // If it's a device from someone else, first check // that our user has signed the other user and then // check if the other user has signed this device. - UserIdentities::Other(device_identity) => { + ReadOnlyUserIdentities::Other(device_identity) => { own_identity.is_identity_signed(device_identity).map_or(false, |_| true) && device_identity.is_device_signed(self).map_or(false, |_| true) } diff --git a/matrix_sdk_crypto/src/identities/manager.rs b/matrix_sdk_crypto/src/identities/manager.rs index e8e7fedc..58763126 100644 --- a/matrix_sdk_crypto/src/identities/manager.rs +++ b/matrix_sdk_crypto/src/identities/manager.rs @@ -29,8 +29,8 @@ use tracing::{trace, warn}; use crate::{ error::OlmResult, identities::{ - MasterPubkey, OwnUserIdentity, ReadOnlyDevice, SelfSigningPubkey, UserIdentities, - UserIdentity, UserSigningPubkey, + MasterPubkey, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities, + ReadOnlyUserIdentity, SelfSigningPubkey, UserSigningPubkey, }, requests::KeysQueryRequest, store::{Changes, DeviceChanges, IdentityChanges, Result as StoreResult, Store}, @@ -246,7 +246,7 @@ impl IdentityManager { let result = if let Some(mut i) = self.store.get_user_identity(user_id).await? { match &mut i { - UserIdentities::Own(ref mut identity) => { + ReadOnlyUserIdentities::Own(ref mut identity) => { let user_signing = if let Some(s) = response.user_signing_keys.get(user_id) { UserSigningPubkey::from(s) @@ -261,7 +261,7 @@ impl IdentityManager { identity.update(master_key, self_signing, user_signing).map(|_| (i, false)) } - UserIdentities::Other(ref mut identity) => { + ReadOnlyUserIdentities::Other(ref mut identity) => { identity.update(master_key, self_signing).map(|_| (i, false)) } } @@ -280,8 +280,8 @@ impl IdentityManager { continue; } - OwnUserIdentity::new(master_key, self_signing, user_signing) - .map(|i| (UserIdentities::Own(i), true)) + ReadOnlyOwnUserIdentity::new(master_key, self_signing, user_signing) + .map(|i| (ReadOnlyUserIdentities::Own(i), true)) } else { warn!( "User identity for our own user {} didn't contain a \ @@ -294,8 +294,8 @@ impl IdentityManager { warn!("User id mismatch in one of the cross signing keys for user {}", user_id); continue; } else { - UserIdentity::new(master_key, self_signing) - .map(|i| (UserIdentities::Other(i), true)) + ReadOnlyUserIdentity::new(master_key, self_signing) + .map(|i| (ReadOnlyUserIdentities::Other(i), true)) }; match result { diff --git a/matrix_sdk_crypto/src/identities/mod.rs b/matrix_sdk_crypto/src/identities/mod.rs index 873cecfa..d90206b3 100644 --- a/matrix_sdk_crypto/src/identities/mod.rs +++ b/matrix_sdk_crypto/src/identities/mod.rs @@ -53,8 +53,8 @@ pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices}; pub(crate) use manager::IdentityManager; use serde::{Deserialize, Deserializer, Serializer}; pub use user::{ - MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity, - UserSigningPubkey, + MasterPubkey, OwnUserIdentity, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities, + ReadOnlyUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity, UserSigningPubkey, }; // These methods are only here because Serialize and Deserialize don't seem to diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index 184d64c2..c124d4e4 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -15,6 +15,7 @@ use std::{ collections::{btree_map::Iter, BTreeMap}, convert::TryFrom, + ops::Deref, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -23,7 +24,10 @@ use std::{ use ruma::{ encryption::{CrossSigningKey, KeyUsage}, - DeviceKeyId, UserId, + events::{ + key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent, + }, + DeviceKeyId, EventId, RoomId, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::to_value; @@ -31,7 +35,153 @@ use serde_json::to_value; use super::{atomic_bool_deserializer, atomic_bool_serializer}; #[cfg(test)] use crate::olm::PrivateCrossSigningIdentity; -use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice}; +use crate::{ + error::SignatureError, olm::Utility, verification::VerificationMachine, CryptoStoreError, + OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest, +}; + +/// Enum over the different user identity types we can have. +#[derive(Debug, Clone)] +pub enum UserIdentities { + /// Our own user identity. + Own(OwnUserIdentity), + /// An identity belonging to another user. + Other(UserIdentity), +} + +impl UserIdentities { + /// Destructure the enum into an `OwnUserIdentity` if it's of the correct + /// type. + pub fn own(self) -> Option { + match self { + Self::Own(i) => Some(i), + _ => None, + } + } + + /// Destructure the enum into an `UserIdentity` if it's of the correct + /// type. + pub fn other(self) -> Option { + match self { + Self::Other(i) => Some(i), + _ => None, + } + } +} + +impl From for UserIdentities { + fn from(i: OwnUserIdentity) -> Self { + Self::Own(i) + } +} + +impl From for UserIdentities { + fn from(i: UserIdentity) -> Self { + Self::Other(i) + } +} + +/// Struct representing a cross signing identity of a user. +/// +/// This is the user identity of a user that isn't our own. Other users will +/// only contain a master key and a self signing key, meaning that only device +/// signatures can be checked with this identity. +/// +/// This struct wraps a read-only version of the struct and allows verifications +/// to be requested to verify our own device with the user identity. +#[derive(Debug, Clone)] +pub struct OwnUserIdentity { + pub(crate) inner: ReadOnlyOwnUserIdentity, + pub(crate) verification_machine: VerificationMachine, +} + +impl Deref for OwnUserIdentity { + type Target = ReadOnlyOwnUserIdentity; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl OwnUserIdentity { + /// Send a verification request to our other devices. + pub async fn request_verification( + &self, + ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { + self.verification_machine.request_self_verification(&self.inner, None).await + } + + /// Send a verification request to our other devices while specifying our + /// supported methods. + /// + /// # Arguments + /// + /// * `methods` - The verification methods that we're supporting. + pub async fn request_verification_with_methods( + &self, + methods: Vec, + ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { + self.verification_machine.request_self_verification(&self.inner, Some(methods)).await + } +} + +/// Struct representing a cross signing identity of a user. +/// +/// This is the user identity of a user that isn't our own. Other users will +/// only contain a master key and a self signing key, meaning that only device +/// signatures can be checked with this identity. +/// +/// This struct wraps a read-only version of the struct and allows verifications +/// to be requested to verify our own device with the user identity. +#[derive(Debug, Clone)] +pub struct UserIdentity { + pub(crate) inner: ReadOnlyUserIdentity, + pub(crate) verification_machine: VerificationMachine, +} + +impl Deref for UserIdentity { + type Target = ReadOnlyUserIdentity; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl UserIdentity { + /// Create a `VerificationRequest` object after the verification request + /// content has been sent out. + pub async fn request_verification( + &self, + room_id: &RoomId, + request_event_id: &EventId, + methods: Option>, + ) -> VerificationRequest { + self.verification_machine + .request_verification(&self.inner, room_id, request_event_id, methods) + .await + } + + /// Send a verification request to the given user. + /// + /// The returned content needs to be sent out into a DM room with the given + /// user. + /// + /// After the content has been sent out a `VerificationRequest` can be + /// started with the [`request_verification`] method. + /// + /// [`request_verification()`]: #method.request_verification + pub async fn verification_request_content( + &self, + methods: Option>, + ) -> KeyVerificationRequestEventContent { + VerificationRequest::request( + self.verification_machine.own_user_id(), + self.verification_machine.own_device_id(), + self.user_id(), + methods, + ) + } +} /// Wrapper for a cross signing key marking it as the master key. /// @@ -358,47 +508,47 @@ impl<'a> IntoIterator for &'a SelfSigningPubkey { /// Enum over the different user identity types we can have. #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum UserIdentities { +pub enum ReadOnlyUserIdentities { /// Our own user identity. - Own(OwnUserIdentity), + Own(ReadOnlyOwnUserIdentity), /// Identities of other users. - Other(UserIdentity), + Other(ReadOnlyUserIdentity), } -impl From for UserIdentities { - fn from(identity: OwnUserIdentity) -> Self { - UserIdentities::Own(identity) +impl From for ReadOnlyUserIdentities { + fn from(identity: ReadOnlyOwnUserIdentity) -> Self { + ReadOnlyUserIdentities::Own(identity) } } -impl From for UserIdentities { - fn from(identity: UserIdentity) -> Self { - UserIdentities::Other(identity) +impl From for ReadOnlyUserIdentities { + fn from(identity: ReadOnlyUserIdentity) -> Self { + ReadOnlyUserIdentities::Other(identity) } } -impl UserIdentities { +impl ReadOnlyUserIdentities { /// The unique user id of this identity. pub fn user_id(&self) -> &UserId { match self { - UserIdentities::Own(i) => i.user_id(), - UserIdentities::Other(i) => i.user_id(), + ReadOnlyUserIdentities::Own(i) => i.user_id(), + ReadOnlyUserIdentities::Other(i) => i.user_id(), } } /// Get the master key of the identity. pub fn master_key(&self) -> &MasterPubkey { match self { - UserIdentities::Own(i) => i.master_key(), - UserIdentities::Other(i) => i.master_key(), + ReadOnlyUserIdentities::Own(i) => i.master_key(), + ReadOnlyUserIdentities::Other(i) => i.master_key(), } } /// Get the self-signing key of the identity. pub fn self_signing_key(&self) -> &SelfSigningPubkey { match self { - UserIdentities::Own(i) => &i.self_signing_key, - UserIdentities::Other(i) => &i.self_signing_key, + ReadOnlyUserIdentities::Own(i) => &i.self_signing_key, + ReadOnlyUserIdentities::Other(i) => &i.self_signing_key, } } @@ -406,32 +556,32 @@ impl UserIdentities { /// own user identity.. pub fn user_signing_key(&self) -> Option<&UserSigningPubkey> { match self { - UserIdentities::Own(i) => Some(&i.user_signing_key), - UserIdentities::Other(_) => None, + ReadOnlyUserIdentities::Own(i) => Some(&i.user_signing_key), + ReadOnlyUserIdentities::Other(_) => None, } } - /// Destructure the enum into an `OwnUserIdentity` if it's of the correct - /// type. - pub fn own(&self) -> Option<&OwnUserIdentity> { + /// Destructure the enum into an `ReadOnlyOwnUserIdentity` if it's of the + /// correct type. + pub fn own(&self) -> Option<&ReadOnlyOwnUserIdentity> { match self { - UserIdentities::Own(i) => Some(i), + ReadOnlyUserIdentities::Own(i) => Some(i), _ => None, } } /// Destructure the enum into an `UserIdentity` if it's of the correct /// type. - pub fn other(&self) -> Option<&UserIdentity> { + pub fn other(&self) -> Option<&ReadOnlyUserIdentity> { match self { - UserIdentities::Other(i) => Some(i), + ReadOnlyUserIdentities::Other(i) => Some(i), _ => None, } } } -impl PartialEq for UserIdentities { - fn eq(&self, other: &UserIdentities) -> bool { +impl PartialEq for ReadOnlyUserIdentities { + fn eq(&self, other: &ReadOnlyUserIdentities) -> bool { self.user_id() == other.user_id() } } @@ -442,13 +592,13 @@ impl PartialEq for UserIdentities { /// only contain a master key and a self signing key, meaning that only device /// signatures can be checked with this identity. #[derive(Debug, Clone, Deserialize, Serialize)] -pub struct UserIdentity { +pub struct ReadOnlyUserIdentity { user_id: Arc, pub(crate) master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, } -impl UserIdentity { +impl ReadOnlyUserIdentity { /// Create a new user identity with the given master and self signing key. /// /// # Arguments @@ -543,7 +693,7 @@ impl UserIdentity { /// This identity can verify other identities as well as devices belonging to /// the identity. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OwnUserIdentity { +pub struct ReadOnlyOwnUserIdentity { user_id: Arc, master_key: MasterPubkey, self_signing_key: SelfSigningPubkey, @@ -555,7 +705,7 @@ pub struct OwnUserIdentity { verified: Arc, } -impl OwnUserIdentity { +impl ReadOnlyOwnUserIdentity { /// Create a new own user identity with the given master, self signing, and /// user signing key. /// @@ -615,7 +765,10 @@ impl OwnUserIdentity { /// /// Returns an empty result if the signature check succeeded, otherwise a /// SignatureError indicating why the check failed. - pub fn is_identity_signed(&self, identity: &UserIdentity) -> Result<(), SignatureError> { + pub fn is_identity_signed( + &self, + identity: &ReadOnlyUserIdentity, + ) -> Result<(), SignatureError> { self.user_signing_key.verify_master_key(&identity.master_key) } @@ -692,7 +845,7 @@ pub(crate) mod test { use matrix_sdk_test::async_test; use ruma::{api::client::r0::keys::get_keys::Response as KeyQueryResponse, user_id}; - use super::{OwnUserIdentity, UserIdentities, UserIdentity}; + use super::{ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities, ReadOnlyUserIdentity}; use crate::{ identities::{ manager::test::{other_key_query, own_key_query}, @@ -710,28 +863,29 @@ pub(crate) mod test { (first, second) } - fn own_identity(response: &KeyQueryResponse) -> OwnUserIdentity { + fn own_identity(response: &KeyQueryResponse) -> ReadOnlyOwnUserIdentity { let user_id = user_id!("@example:localhost"); let master_key = response.master_keys.get(&user_id).unwrap(); let user_signing = response.user_signing_keys.get(&user_id).unwrap(); let self_signing = response.self_signing_keys.get(&user_id).unwrap(); - OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap() + ReadOnlyOwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()) + .unwrap() } - pub(crate) fn get_own_identity() -> OwnUserIdentity { + pub(crate) fn get_own_identity() -> ReadOnlyOwnUserIdentity { own_identity(&own_key_query()) } - pub(crate) fn get_other_identity() -> UserIdentity { + pub(crate) fn get_other_identity() -> ReadOnlyUserIdentity { let user_id = user_id!("@example2:localhost"); let response = other_key_query(); let master_key = response.master_keys.get(&user_id).unwrap(); let self_signing = response.self_signing_keys.get(&user_id).unwrap(); - UserIdentity::new(master_key.into(), self_signing.into()).unwrap() + ReadOnlyUserIdentity::new(master_key.into(), self_signing.into()).unwrap() } #[test] @@ -743,7 +897,8 @@ pub(crate) mod test { let user_signing = response.user_signing_keys.get(&user_id).unwrap(); let self_signing = response.self_signing_keys.get(&user_id).unwrap(); - OwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()).unwrap(); + ReadOnlyOwnUserIdentity::new(master_key.into(), self_signing.into(), user_signing.into()) + .unwrap(); } #[test] @@ -773,7 +928,7 @@ pub(crate) mod test { verification_machine: verification_machine.clone(), private_identity: private_identity.clone(), own_identity: Some(identity.clone()), - device_owner_identity: Some(UserIdentities::Own(identity.clone())), + device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())), }; let second = Device { @@ -781,7 +936,7 @@ pub(crate) mod test { verification_machine, private_identity, own_identity: Some(identity.clone()), - device_owner_identity: Some(UserIdentities::Own(identity.clone())), + device_owner_identity: Some(ReadOnlyUserIdentities::Own(identity.clone())), }; assert!(!second.is_locally_trusted()); diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index dcd4db02..a3d7a244 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -45,7 +45,8 @@ pub use file_encryption::{ DecryptorError, EncryptionInfo, KeyExportError, }; pub use identities::{ - Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, UserDevices, UserIdentities, UserIdentity, + Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, ReadOnlyOwnUserIdentity, + ReadOnlyUserIdentities, ReadOnlyUserIdentity, UserDevices, UserIdentities, UserIdentity, }; pub use machine::OlmMachine; pub use matrix_qrcode; diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index 68b4426b..f2f4f22e 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -46,7 +46,7 @@ use tracing::{debug, error, info, trace, warn}; use crate::store::sled::SledStore; use crate::{ error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult}, - identities::{Device, IdentityManager, UserDevices}, + identities::{user::UserIdentities, Device, IdentityManager, UserDevices}, key_request::KeyRequestMachine, olm::{ Account, EncryptionSettings, ExportedRoomKey, GroupSessionKey, IdentityKeys, @@ -1052,6 +1052,18 @@ impl OlmMachine { self.store.get_device(user_id, device_id).await } + /// Get the cross signing user identity of a user. + /// + /// # Arguments + /// + /// * `user_id` - The unique id of the user that the identity belongs to + /// + /// Returns a `UserIdentities` enum if one is found and the crypto store + /// didn't throw an error. + pub async fn get_identity(&self, user_id: &UserId) -> StoreResult> { + self.store.get_identity(user_id).await + } + /// Get a map holding all the devices of an user. /// /// # Arguments diff --git a/matrix_sdk_crypto/src/olm/signing/mod.rs b/matrix_sdk_crypto/src/olm/signing/mod.rs index 523c274b..469cdbce 100644 --- a/matrix_sdk_crypto/src/olm/signing/mod.rs +++ b/matrix_sdk_crypto/src/olm/signing/mod.rs @@ -34,7 +34,7 @@ use serde_json::Error as JsonError; use crate::{ error::SignatureError, identities::MasterPubkey, requests::UploadSigningKeysRequest, - OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice, UserIdentity, + ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity, ReadOnlyUserIdentity, }; /// Private cross signing identity. @@ -117,7 +117,9 @@ impl PrivateCrossSigningIdentity { } } - pub(crate) async fn to_public_identity(&self) -> Result { + pub(crate) async fn to_public_identity( + &self, + ) -> Result { let master = self .master_key .lock() @@ -142,7 +144,7 @@ impl PrivateCrossSigningIdentity { .ok_or(SignatureError::MissingSigningKey)? .public_key .clone(); - let identity = OwnUserIdentity::new(master, self_signing, user_signing)?; + let identity = ReadOnlyOwnUserIdentity::new(master, self_signing, user_signing)?; identity.mark_as_verified(); Ok(identity) @@ -151,7 +153,7 @@ impl PrivateCrossSigningIdentity { /// Sign the given public user identity with this private identity. pub(crate) async fn sign_user( &self, - user_identity: &UserIdentity, + user_identity: &ReadOnlyUserIdentity, ) -> Result { let signed_keys = self .user_signing_key @@ -407,7 +409,7 @@ mod test { use super::{PrivateCrossSigningIdentity, Signing}; use crate::{ - identities::{ReadOnlyDevice, UserIdentity}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentity}, olm::ReadOnlyAccount, }; @@ -518,7 +520,7 @@ mod test { let bob_account = ReadOnlyAccount::new(&user_id!("@bob:localhost"), "DEVICEID".into()); let (bob_private, _, _) = PrivateCrossSigningIdentity::new_with_account(&bob_account).await; - let mut bob_public = UserIdentity::from_private(&bob_private).await; + let mut bob_public = ReadOnlyUserIdentity::from_private(&bob_private).await; let user_signing = identity.user_signing_key.lock().await; let user_signing = user_signing.as_ref().unwrap(); diff --git a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs index 00589e6d..61ba5bcd 100644 --- a/matrix_sdk_crypto/src/olm/signing/pk_signing.rs +++ b/matrix_sdk_crypto/src/olm/signing/pk_signing.rs @@ -37,7 +37,7 @@ use crate::{ error::SignatureError, identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey}, utilities::{decode_url_safe as decode, encode_url_safe as encode, DecodeError}, - UserIdentity, + ReadOnlyUserIdentity, }; const NONCE_SIZE: usize = 12; @@ -186,7 +186,7 @@ impl UserSigning { pub async fn sign_user( &self, - user: &UserIdentity, + user: &ReadOnlyUserIdentity, ) -> Result>, SignatureError> { let user_master: &CrossSigningKey = user.master_key().as_ref(); let signature = self.inner.sign_json(serde_json::to_value(user_master)?).await?; diff --git a/matrix_sdk_crypto/src/store/memorystore.rs b/matrix_sdk_crypto/src/store/memorystore.rs index 348b0c79..ef52fd44 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -26,7 +26,7 @@ use super::{ Changes, CryptoStore, InboundGroupSession, ReadOnlyAccount, Result, Session, }; use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, key_request::OutgoingKeyRequest, olm::{OutboundGroupSession, PrivateCrossSigningIdentity}, }; @@ -44,7 +44,7 @@ pub struct MemoryStore { users_for_key_query: Arc>, olm_hashes: Arc>>, devices: DeviceStore, - identities: Arc>, + identities: Arc>, outgoing_key_requests: Arc>, key_requests_by_info: Arc>, } @@ -215,7 +215,7 @@ impl CryptoStore for MemoryStore { Ok(self.devices.user_devices(user_id)) } - async fn get_user_identity(&self, user_id: &UserId) -> Result> { + async fn get_user_identity(&self, user_id: &UserId) -> Result> { #[allow(clippy::map_clone)] Ok(self.identities.get(user_id).map(|i| i.clone())) } diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index 37c413cb..74f5aacf 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -66,7 +66,10 @@ use thiserror::Error; pub use self::sled::SledStore; use crate::{ error::SessionUnpicklingError, - identities::{Device, ReadOnlyDevice, UserDevices, UserIdentities}, + identities::{ + user::{OwnUserIdentity, UserIdentities, UserIdentity}, + Device, ReadOnlyDevice, ReadOnlyUserIdentities, UserDevices, + }, key_request::OutgoingKeyRequest, olm::{ InboundGroupSession, OlmMessageHash, OutboundGroupSession, PrivateCrossSigningIdentity, @@ -109,8 +112,8 @@ pub struct Changes { #[derive(Debug, Clone, Default)] #[allow(missing_docs)] pub struct IdentityChanges { - pub new: Vec, - pub changed: Vec, + pub new: Vec, + pub changed: Vec, } #[derive(Debug, Clone, Default)] @@ -215,8 +218,8 @@ impl Store { device_id: &DeviceId, ) -> Result> { let own_identity = - self.get_user_identity(&self.user_id).await?.map(|i| i.own().cloned()).flatten(); - let device_owner_identity = self.get_user_identity(user_id).await?; + self.inner.get_user_identity(&self.user_id).await?.map(|i| i.own().cloned()).flatten(); + let device_owner_identity = self.inner.get_user_identity(user_id).await?; Ok(self.inner.get_device(user_id, device_id).await?.map(|d| Device { inner: d, @@ -226,6 +229,20 @@ impl Store { device_owner_identity, })) } + + pub async fn get_identity(&self, user_id: &UserId) -> Result> { + Ok(self.inner.get_user_identity(user_id).await?.map(|i| match i { + ReadOnlyUserIdentities::Own(i) => OwnUserIdentity { + inner: i, + verification_machine: self.verification_machine.clone(), + } + .into(), + ReadOnlyUserIdentities::Other(i) => { + UserIdentity { inner: i, verification_machine: self.verification_machine.clone() } + .into() + } + })) + } } impl Deref for Store { @@ -388,7 +405,7 @@ pub trait CryptoStore: AsyncTraitDeps { /// # Arguments /// /// * `user_id` - The user for which we should get the identity. - async fn get_user_identity(&self, user_id: &UserId) -> Result>; + async fn get_user_identity(&self, user_id: &UserId) -> Result>; /// Check if a hash for an Olm message stored in the database. async fn is_message_known(&self, message_hash: &OlmMessageHash) -> Result; diff --git a/matrix_sdk_crypto/src/store/sled.rs b/matrix_sdk_crypto/src/store/sled.rs index b803496c..9ed76e07 100644 --- a/matrix_sdk_crypto/src/store/sled.rs +++ b/matrix_sdk_crypto/src/store/sled.rs @@ -35,7 +35,7 @@ use super::{ ReadOnlyAccount, Result, Session, }; use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, key_request::OutgoingKeyRequest, olm::{OutboundGroupSession, PickledInboundGroupSession, PrivateCrossSigningIdentity}, }; @@ -669,7 +669,7 @@ impl CryptoStore for SledStore { .collect() } - async fn get_user_identity(&self, user_id: &UserId) -> Result> { + async fn get_user_identity(&self, user_id: &UserId) -> Result> { Ok(self .identities .get(user_id.encode())? diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 330f02cc..374e4410 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -20,9 +20,13 @@ use std::{ use dashmap::DashMap; use matrix_sdk_common::{locks::Mutex, uuid::Uuid}; use ruma::{ - events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent}, + events::{ + key::verification::VerificationMethod, AnyToDeviceEvent, AnyToDeviceEventContent, + ToDeviceEvent, + }, serde::Raw, - DeviceId, MilliSecondsSinceUnixEpoch, UserId, + to_device::DeviceIdOrAllDevices, + DeviceId, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use tracing::{info, trace, warn}; @@ -37,8 +41,8 @@ use crate::{ olm::PrivateCrossSigningIdentity, requests::OutgoingRequest, store::{CryptoStore, CryptoStoreError}, - OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, RoomMessageRequest, - ToDeviceRequest, + OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity, + ReadOnlyUserIdentity, RoomMessageRequest, ToDeviceRequest, }; #[derive(Clone, Debug)] @@ -65,6 +69,69 @@ impl VerificationMachine { } } + pub(crate) fn own_user_id(&self) -> &UserId { + self.account.user_id() + } + + pub(crate) fn own_device_id(&self) -> &DeviceId { + self.account.device_id() + } + + pub(crate) async fn request_self_verification( + &self, + identity: &ReadOnlyOwnUserIdentity, + methods: Option>, + ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { + let flow_id = FlowId::from(Uuid::new_v4().to_string()); + + let verification = VerificationRequest::new( + self.verifications.clone(), + self.account.clone(), + self.private_identity.lock().await.clone(), + self.store.clone(), + flow_id, + identity.user_id(), + methods, + ); + + // TODO get all the device ids of the user instead of using AllDevices + // make sure to remember this so we can cancel once someone picks up + let request: OutgoingVerificationRequest = ToDeviceRequest::new( + identity.user_id(), + DeviceIdOrAllDevices::AllDevices, + AnyToDeviceEventContent::KeyVerificationRequest(verification.request_to_device()), + ) + .into(); + + self.insert_request(verification.clone()); + + Ok((verification, request)) + } + + pub async fn request_verification( + &self, + identity: &ReadOnlyUserIdentity, + room_id: &RoomId, + request_event_id: &EventId, + methods: Option>, + ) -> VerificationRequest { + let flow_id = FlowId::InRoom(room_id.to_owned(), request_event_id.to_owned()); + + let request = VerificationRequest::new( + self.verifications.clone(), + self.account.clone(), + self.private_identity.lock().await.clone(), + self.store.clone(), + flow_id, + identity.user_id(), + methods, + ); + + self.insert_request(request.clone()); + + request + } + pub async fn start_sas( &self, device: ReadOnlyDevice, diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index b77d758b..025fca1e 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -44,7 +44,7 @@ use crate::{ error::SignatureError, olm::PrivateCrossSigningIdentity, store::{Changes, CryptoStore}, - CryptoStoreError, LocalTrust, ReadOnlyDevice, UserIdentities, + CryptoStoreError, LocalTrust, ReadOnlyDevice, ReadOnlyUserIdentities, }; /// An enum over the different verification types the SDK supports. @@ -144,7 +144,7 @@ impl From for Verification { #[derive(Clone, Debug)] pub struct Done { verified_devices: Arc<[ReadOnlyDevice]>, - verified_master_keys: Arc<[UserIdentities]>, + verified_master_keys: Arc<[ReadOnlyUserIdentities]>, } impl Done { @@ -277,7 +277,7 @@ pub struct IdentitiesBeingVerified { private_identity: PrivateCrossSigningIdentity, store: Arc, device_being_verified: ReadOnlyDevice, - identity_being_verified: Option, + identity_being_verified: Option, } impl IdentitiesBeingVerified { @@ -308,7 +308,7 @@ impl IdentitiesBeingVerified { pub async fn mark_as_done( &self, verified_devices: Option<&[ReadOnlyDevice]>, - verified_identities: Option<&[UserIdentities]>, + verified_identities: Option<&[ReadOnlyUserIdentities]>, ) -> Result { let device = self.mark_device_as_verified(verified_devices).await?; let identity = self.mark_identity_as_verified(verified_identities).await?; @@ -415,8 +415,8 @@ impl IdentitiesBeingVerified { async fn mark_identity_as_verified( &self, - verified_identities: Option<&[UserIdentities]>, - ) -> Result, CryptoStoreError> { + verified_identities: Option<&[ReadOnlyUserIdentities]>, + ) -> Result, CryptoStoreError> { // If there wasn't an identity available during the verification flow // return early as there's nothing to do. if self.identity_being_verified.is_none() { @@ -437,7 +437,7 @@ impl IdentitiesBeingVerified { "Marking the user identity of as verified." ); - if let UserIdentities::Own(i) = &identity { + if let ReadOnlyUserIdentities::Own(i) = &identity { i.mark_as_verified(); } diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 0dd4b576..9ded7336 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -45,8 +45,8 @@ use super::{ use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, store::CryptoStore, - CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, - ToDeviceRequest, UserIdentities, + CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, ReadOnlyUserIdentities, + RoomMessageRequest, ToDeviceRequest, }; const SECRET_SIZE: usize = 16; @@ -518,7 +518,7 @@ impl QrVerification { ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone()) })?; - let check_master_key = |key, identity: &UserIdentities| { + let check_master_key = |key, identity: &ReadOnlyUserIdentities| { let master_key = identity.master_key().get_first_key().ok_or_else(|| { ScanError::MissingCrossSigningIdentity(identity.user_id().clone()) })?; @@ -719,7 +719,7 @@ impl QrState { self.state.as_content(flow_id) } - fn verified_identities(&self) -> (Arc<[ReadOnlyDevice]>, Arc<[UserIdentities]>) { + fn verified_identities(&self) -> (Arc<[ReadOnlyDevice]>, Arc<[ReadOnlyUserIdentities]>) { (self.state.verified_devices.clone(), self.state.verified_master_keys.clone()) } } @@ -729,7 +729,7 @@ impl QrState { self, _: &DoneContent, verified_device: Option<&ReadOnlyDevice>, - verified_identity: Option<&UserIdentities>, + verified_identity: Option<&ReadOnlyUserIdentities>, ) -> QrState { let devices: Vec<_> = verified_device.into_iter().cloned().collect(); let identities: Vec<_> = verified_identity.into_iter().cloned().collect(); @@ -768,7 +768,7 @@ impl QrState { self, _: &DoneContent, verified_device: Option<&ReadOnlyDevice>, - verified_identity: Option<&UserIdentities>, + verified_identity: Option<&ReadOnlyUserIdentities>, ) -> QrState { let devices: Vec<_> = verified_device.into_iter().cloned().collect(); let identities: Vec<_> = verified_identity.into_iter().cloned().collect(); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index e84193e1..7c224d55 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(dead_code)] - use std::sync::{Arc, Mutex}; use matrix_qrcode::QrVerificationData; @@ -31,7 +29,7 @@ use ruma::{ AnyMessageEventContent, AnyToDeviceEventContent, }, to_device::DeviceIdOrAllDevices, - DeviceId, DeviceIdBox, DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, + DeviceId, DeviceIdBox, DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use tracing::{info, trace, warn}; @@ -46,8 +44,8 @@ use super::{ use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, store::CryptoStore, - CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, RoomMessageRequest, Sas, - ToDeviceRequest, UserIdentities, + CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, ReadOnlyUserIdentities, + RoomMessageRequest, Sas, ToDeviceRequest, }; const SUPPORTED_METHODS: &[VerificationMethod] = &[ @@ -79,41 +77,10 @@ impl VerificationRequest { account: ReadOnlyAccount, private_cross_signing_identity: PrivateCrossSigningIdentity, store: Arc, - room_id: &RoomId, - event_id: &EventId, + flow_id: FlowId, other_user: &UserId, + methods: Option>, ) -> Self { - let flow_id = (room_id.to_owned(), event_id.to_owned()).into(); - - let inner = Mutex::new(InnerRequest::Created(RequestState::new( - account.clone(), - private_cross_signing_identity, - cache.clone(), - store, - other_user, - &flow_id, - ))) - .into(); - - Self { - account, - verification_cache: cache, - flow_id: flow_id.into(), - inner, - other_user_id: other_user.to_owned().into(), - we_started: true, - } - } - - pub(crate) fn new_to_device( - cache: VerificationCache, - account: ReadOnlyAccount, - private_cross_signing_identity: PrivateCrossSigningIdentity, - store: Arc, - other_user: &UserId, - ) -> Self { - let flow_id = Uuid::new_v4().to_string().into(); - let inner = Mutex::new(InnerRequest::Created(RequestState::new( account.clone(), private_cross_signing_identity, @@ -121,6 +88,7 @@ impl VerificationRequest { store, other_user, &flow_id, + methods, ))) .into(); @@ -138,11 +106,19 @@ impl VerificationRequest { /// verification from the other side. This should be used only for /// self-verifications and it should be sent to the specific device that we /// want to verify. - pub fn request_to_device(&self) -> RequestToDeviceEventContent { + pub(crate) fn request_to_device(&self) -> RequestToDeviceEventContent { + let inner = self.inner.lock().unwrap(); + + let methods = if let InnerRequest::Created(c) = &*inner { + c.state.our_methods.clone() + } else { + SUPPORTED_METHODS.to_vec() + }; + RequestToDeviceEventContent::new( self.account.device_id().into(), self.flow_id().as_str().to_string(), - SUPPORTED_METHODS.to_vec(), + methods, MilliSecondsSinceUnixEpoch::now(), ) } @@ -155,6 +131,7 @@ impl VerificationRequest { own_user_id: &UserId, own_device_id: &DeviceId, other_user_id: &UserId, + methods: Option>, ) -> KeyVerificationRequestEventContent { KeyVerificationRequestEventContent::new( format!( @@ -163,7 +140,7 @@ impl VerificationRequest { key verification to verify keys.", own_user_id ), - SUPPORTED_METHODS.to_vec(), + methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec()), own_device_id.into(), other_user_id.to_owned(), ) @@ -376,6 +353,15 @@ impl VerificationRequest { /// Cancel the verification request pub fn cancel(&self) -> Option { + if let Some(verification) = + self.verification_cache.get(self.other_user(), self.flow_id().as_str()) + { + match verification { + crate::Verification::SasV1(s) => s.cancel(), + crate::Verification::QrV1(q) => q.cancel(), + }; + } + let mut inner = self.inner.lock().unwrap(); inner.cancel(true, &CancelCode::User); @@ -398,12 +384,20 @@ impl VerificationRequest { pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) { let mut inner = self.inner.lock().unwrap(); - if let InnerRequest::Created(s) = &*inner { - if sender == self.own_user_id() && content.from_device() == self.account.device_id() { - *inner = InnerRequest::Passive(s.clone().into_passive(content)) - } else { + match &*inner { + InnerRequest::Created(s) => { *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); } + InnerRequest::Requested(s) => { + if sender == self.own_user_id() && content.from_device() != self.account.device_id() + { + *inner = InnerRequest::Passive(s.clone().into_passive(content)) + } + } + InnerRequest::Ready(_) + | InnerRequest::Passive(_) + | InnerRequest::Done(_) + | InnerRequest::Cancelled(_) => {} } } @@ -512,17 +506,6 @@ impl InnerRequest { } } - fn other_user_id(&self) -> &UserId { - match self { - InnerRequest::Created(s) => &s.other_user_id, - InnerRequest::Requested(s) => &s.other_user_id, - InnerRequest::Ready(s) => &s.other_user_id, - InnerRequest::Passive(s) => &s.other_user_id, - InnerRequest::Done(s) => &s.other_user_id, - InnerRequest::Cancelled(s) => &s.other_user_id, - } - } - fn accept(&mut self, methods: Vec) -> Option { if let InnerRequest::Requested(s) = self { let (state, content) = s.clone().accept(methods); @@ -569,20 +552,6 @@ impl InnerRequest { InnerRequest::Cancelled(_) => Ok(None), } } - - fn to_started_sas( - &self, - content: &StartContent, - other_device: ReadOnlyDevice, - other_identity: Option, - we_started: bool, - ) -> Result, OutgoingContent> { - if let InnerRequest::Ready(s) = self { - Ok(Some(s.to_started_sas(content, other_device, other_identity, we_started)?)) - } else { - Ok(None) - } - } } #[derive(Clone, Debug)] @@ -638,30 +607,21 @@ impl RequestState { store: Arc, other_user_id: &UserId, flow_id: &FlowId, + methods: Option>, ) -> Self { + let our_methods = methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec()); + Self { account, other_user_id: other_user_id.to_owned(), private_cross_signing_identity: private_identity, - state: Created { our_methods: SUPPORTED_METHODS.to_vec() }, + state: Created { our_methods }, verification_cache: cache, store, flow_id: flow_id.to_owned().into(), } } - fn into_passive(self, content: &ReadyContent) -> RequestState { - RequestState { - account: self.account, - flow_id: self.flow_id, - verification_cache: self.verification_cache, - private_cross_signing_identity: self.private_cross_signing_identity, - store: self.store, - other_user_id: self.other_user_id, - state: Passive { other_device_id: content.from_device().to_owned() }, - } - } - fn into_ready(self, _sender: &UserId, content: &ReadyContent) -> RequestState { // TODO check the flow id, and that the methods match what we suggested. RequestState { @@ -720,6 +680,18 @@ impl RequestState { } } + fn into_passive(self, content: &ReadyContent) -> RequestState { + RequestState { + account: self.account, + flow_id: self.flow_id, + verification_cache: self.verification_cache, + private_cross_signing_identity: self.private_cross_signing_identity, + store: self.store, + other_user_id: self.other_user_id, + state: Passive { other_device_id: content.from_device().to_owned() }, + } + } + fn accept(self, methods: Vec) -> (RequestState, OutgoingContent) { let state = RequestState { account: self.account.clone(), @@ -776,7 +748,7 @@ impl RequestState { &self, content: &StartContent<'a>, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, we_started: bool, ) -> Result { Sas::from_start_event( @@ -827,7 +799,7 @@ impl RequestState { let verification = if let Some(identity) = &identites.identity_being_verified { match &identity { - UserIdentities::Own(i) => { + ReadOnlyUserIdentities::Own(i) => { if let Some(master_key) = i.master_key().get_first_key() { if identites.can_sign_devices().await { if let Some(device_key) = @@ -870,7 +842,7 @@ impl RequestState { None } } - UserIdentities::Other(i) => { + ReadOnlyUserIdentities::Other(i) => { if let Some(other_master) = i.master_key().get_first_key() { // TODO we can get the master key from the public // identity if we don't have the private one and we @@ -1117,20 +1089,21 @@ 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 content = + VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id(), None); + + let flow_id = FlowId::InRoom(room_id, event_id); let bob_request = VerificationRequest::new( VerificationCache::new(), bob, bob_identity, bob_store.into(), - &room_id, - &event_id, + flow_id.clone(), &alice_id(), + None, ); - let flow_id = FlowId::from((room_id, event_id)); - let alice_request = VerificationRequest::from_request( VerificationCache::new(), alice, @@ -1174,20 +1147,20 @@ mod test { changes.devices.new.push(alice_device.clone()); bob_store.save_changes(changes).await.unwrap(); - let content = VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id()); + let content = + VerificationRequest::request(bob.user_id(), bob.device_id(), &alice_id(), None); + let flow_id = FlowId::from((room_id, event_id)); let bob_request = VerificationRequest::new( VerificationCache::new(), bob, bob_identity, bob_store.into(), - &room_id, - &event_id, + flow_id.clone(), &alice_id(), + None, ); - let flow_id = FlowId::from((room_id, event_id)); - let alice_request = VerificationRequest::from_request( VerificationCache::new(), alice, @@ -1240,12 +1213,16 @@ mod test { changes.devices.new.push(alice_device.clone()); bob_store.save_changes(changes).await.unwrap(); - let bob_request = VerificationRequest::new_to_device( + let flow_id = FlowId::from("TEST_FLOW_ID".to_owned()); + + let bob_request = VerificationRequest::new( VerificationCache::new(), bob, bob_identity, bob_store.into(), + flow_id, &alice_id(), + None, ); let content = bob_request.request_to_device(); diff --git a/matrix_sdk_crypto/src/verification/sas/helpers.rs b/matrix_sdk_crypto/src/verification/sas/helpers.rs index 868df891..f39869b0 100644 --- a/matrix_sdk_crypto/src/verification/sas/helpers.rs +++ b/matrix_sdk_crypto/src/verification/sas/helpers.rs @@ -31,7 +31,7 @@ use tracing::{trace, warn}; use super::{FlowId, OutgoingContent}; use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, utilities::encode, verification::event_enums::{MacContent, StartContent}, ReadOnlyAccount, @@ -41,7 +41,7 @@ use crate::{ pub struct SasIds { pub account: ReadOnlyAccount, pub other_device: ReadOnlyDevice, - pub other_identity: Option, + pub other_identity: Option, } /// Calculate the commitment for a accept event from the public key and the @@ -182,7 +182,7 @@ pub fn receive_mac_event( flow_id: &str, sender: &UserId, content: &MacContent, -) -> Result<(Vec, Vec), CancelCode> { +) -> Result<(Vec, Vec), CancelCode> { let mut verified_devices = Vec::new(); let mut verified_identities = Vec::new(); diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index fd6fe7a1..04267442 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -29,7 +29,7 @@ use super::{ FlowId, }; use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, verification::{ event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent}, Cancelled, Done, @@ -55,7 +55,7 @@ impl InnerSas { pub fn start( account: ReadOnlyAccount, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, transaction_id: Option, ) -> (InnerSas, OutgoingContent) { let sas = SasState::::new(account, other_device, other_identity, transaction_id); @@ -131,7 +131,7 @@ impl InnerSas { room_id: RoomId, account: ReadOnlyAccount, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, ) -> (InnerSas, OutgoingContent) { let sas = SasState::::new_in_room( room_id, @@ -149,7 +149,7 @@ impl InnerSas { other_device: ReadOnlyDevice, flow_id: FlowId, content: &StartContent, - other_identity: Option, + other_identity: Option, started_from_request: bool, ) -> Result { match SasState::::from_start_event( @@ -204,7 +204,9 @@ impl InnerSas { InnerSas::WeAccepted(s) => s.cancel(cancelled_by_us, code), InnerSas::KeyReceived(s) => s.cancel(cancelled_by_us, code), InnerSas::MacReceived(s) => s.cancel(cancelled_by_us, code), - _ => return (self, None), + InnerSas::Confirmed(s) => s.cancel(cancelled_by_us, code), + InnerSas::WaitingForDone(s) => s.cancel(cancelled_by_us, code), + InnerSas::Done(_) | InnerSas::Cancelled(_) => return (self, None), }; let content = sas.as_content(); @@ -423,7 +425,7 @@ impl InnerSas { } } - pub fn verified_identities(&self) -> Option> { + pub fn verified_identities(&self) -> Option> { if let InnerSas::Done(s) = self { Some(s.verified_identities()) } else { diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index eb4b78af..bdf1f107 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -41,7 +41,7 @@ use super::{ FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, olm::PrivateCrossSigningIdentity, requests::{OutgoingVerificationRequest, RoomMessageRequest}, store::{CryptoStore, CryptoStoreError}, @@ -151,7 +151,7 @@ impl Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, store: Arc, - other_identity: Option, + other_identity: Option, we_started: bool, ) -> Sas { let flow_id = inner_sas.verification_flow_id(); @@ -187,7 +187,7 @@ impl Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, store: Arc, - other_identity: Option, + other_identity: Option, transaction_id: Option, we_started: bool, ) -> (Sas, OutgoingContent) { @@ -230,7 +230,7 @@ impl Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, store: Arc, - other_identity: Option, + other_identity: Option, we_started: bool, ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start_in_room( @@ -273,7 +273,7 @@ impl Sas { account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, started_from_request: bool, we_started: bool, ) -> Result { @@ -503,7 +503,7 @@ impl Sas { self.inner.lock().unwrap().verified_devices() } - pub(crate) fn verified_identities(&self) -> Option> { + pub(crate) fn verified_identities(&self) -> Option> { self.inner.lock().unwrap().verified_identities() } diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index af5dad17..f4e77c2a 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -52,7 +52,7 @@ use super::{ OutgoingContent, }; use crate::{ - identities::{ReadOnlyDevice, UserIdentities}, + identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, verification::{ event_enums::{ AcceptContent, DoneContent, KeyContent, MacContent, OwnedAcceptContent, @@ -277,7 +277,7 @@ pub struct MacReceived { we_started: bool, their_pubkey: String, verified_devices: Arc<[ReadOnlyDevice]>, - verified_master_keys: Arc<[UserIdentities]>, + verified_master_keys: Arc<[ReadOnlyUserIdentities]>, pub accepted_protocols: Arc, } @@ -287,7 +287,7 @@ pub struct MacReceived { #[derive(Clone, Debug)] pub struct WaitingForDone { verified_devices: Arc<[ReadOnlyDevice]>, - verified_master_keys: Arc<[UserIdentities]>, + verified_master_keys: Arc<[ReadOnlyUserIdentities]>, } impl SasState { @@ -362,7 +362,7 @@ impl SasState { pub fn new( account: ReadOnlyAccount, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, transaction_id: Option, ) -> SasState { let started_from_request = transaction_id.is_some(); @@ -388,7 +388,7 @@ impl SasState { event_id: EventId, account: ReadOnlyAccount, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, ) -> SasState { let flow_id = FlowId::InRoom(room_id, event_id); Self::new_helper(flow_id, account, other_device, other_identity, false) @@ -398,7 +398,7 @@ impl SasState { flow_id: FlowId, account: ReadOnlyAccount, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, started_from_request: bool, ) -> SasState { SasState { @@ -497,7 +497,7 @@ impl SasState { pub fn from_start_event( account: ReadOnlyAccount, other_device: ReadOnlyDevice, - other_identity: Option, + other_identity: Option, flow_id: FlowId, content: &StartContent, started_from_request: bool, @@ -1096,7 +1096,7 @@ impl SasState { } /// Get the list of verified identities. - pub fn verified_identities(&self) -> Arc<[UserIdentities]> { + pub fn verified_identities(&self) -> Arc<[ReadOnlyUserIdentities]> { self.state.verified_master_keys.clone() } } From ae37e6ec9d37a59c2f490e13d9bddc55827ab2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 11:38:10 +0200 Subject: [PATCH 64/88] crypto: Add a state getter where we scanned the QR code --- matrix_sdk_crypto/src/verification/qrcode.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 9ded7336..455bec20 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -165,6 +165,12 @@ impl QrVerification { self.identities.is_self_verification() } + /// Have we successfully scanned the QR code and are able to send a + /// reciprocation event. + pub fn reciprocated(&self) -> bool { + matches!(&*self.state.lock().unwrap(), InnerState::Reciprocated(_)) + } + /// Get the unique ID that identifies this QR code verification flow. pub fn flow_id(&self) -> &FlowId { &self.flow_id From 5c9840daf829eb552f253ca29429884041af20f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 11:39:25 +0200 Subject: [PATCH 65/88] crypto: Return a request when we start QR code verification instead of the content --- matrix_sdk_crypto/src/verification/qrcode.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 455bec20..605b248c 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -245,9 +245,11 @@ impl QrVerification { /// /// This will return some `OutgoingContent` if the object is in the correct /// state to start the verification flow, otherwise `None`. - pub fn reciprocate(&self) -> Option { + pub fn reciprocate(&self) -> Option { match &*self.state.lock().unwrap() { - InnerState::Reciprocated(s) => Some(s.as_content(self.flow_id())), + InnerState::Reciprocated(s) => { + Some(self.content_to_request(s.as_content(self.flow_id()))) + } InnerState::Created(_) | InnerState::Scanned(_) | InnerState::Confirmed(_) From 76d57baa11b5967b03502595bde06a32ca6a8867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 11:40:21 +0200 Subject: [PATCH 66/88] crypto: Add the verification to the cache after we scan a QR code --- matrix_sdk_crypto/src/verification/qrcode.rs | 3 +- .../src/verification/requests.rs | 28 ++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index 605b248c..d460f54b 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -947,7 +947,8 @@ mod test { .await .unwrap(); - let content = bob_verification.reciprocate().unwrap(); + let request = bob_verification.reciprocate().unwrap(); + let content = OutgoingContent::from(request); let content = StartContent::try_from(&content).unwrap(); alice_verification.receive_reciprocation(&content); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 7c224d55..5c987a13 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -271,19 +271,21 @@ impl VerificationRequest { let state = self.inner.lock().unwrap(); if let InnerRequest::Ready(r) = &*state { - Ok(Some( - QrVerification::from_scan( - r.store.clone(), - r.account.clone(), - r.private_cross_signing_identity.clone(), - r.other_user_id.clone(), - r.state.other_device_id.clone(), - r.flow_id.as_ref().to_owned(), - data, - self.we_started, - ) - .await?, - )) + let qr_verification = QrVerification::from_scan( + r.store.clone(), + r.account.clone(), + r.private_cross_signing_identity.clone(), + r.other_user_id.clone(), + r.state.other_device_id.clone(), + r.flow_id.as_ref().to_owned(), + data, + self.we_started, + ) + .await?; + + self.verification_cache.insert_qr(qr_verification.clone()); + + Ok(Some(qr_verification)) } else { Ok(None) } From 4e5cc036735845278b59e5fb576bc08e235dcd7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 11:50:12 +0200 Subject: [PATCH 67/88] crypto: Only go into a done verification request if we're in the correct state --- matrix_sdk_crypto/src/verification/requests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 5c987a13..1c661e82 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -521,12 +521,12 @@ impl InnerRequest { fn receive_done(&mut self, content: &DoneContent) { *self = InnerRequest::Done(match self { - InnerRequest::Created(s) => s.clone().into_done(content), - InnerRequest::Requested(s) => s.clone().into_done(content), InnerRequest::Ready(s) => s.clone().into_done(content), InnerRequest::Passive(s) => s.clone().into_done(content), - InnerRequest::Done(s) => s.clone().into_done(content), - InnerRequest::Cancelled(_) => return, + InnerRequest::Done(_) + | InnerRequest::Created(_) + | InnerRequest::Requested(_) + | InnerRequest::Cancelled(_) => return, }) } From b0e8f124265f1359fb2190bd66fd8c08d1ac9ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 13:53:47 +0200 Subject: [PATCH 68/88] crypto: Cancel the verification request if the child flow gets cancelled --- matrix_sdk_crypto/src/verification/machine.rs | 2 +- matrix_sdk_crypto/src/verification/qrcode.rs | 28 ++++++++-- .../src/verification/requests.rs | 56 +++++++++++++++++-- matrix_sdk_crypto/src/verification/sas/mod.rs | 20 ++++++- 4 files changed, 92 insertions(+), 14 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 374e4410..3a89b070 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -419,7 +419,7 @@ impl VerificationMachine { private_identity, device, identity, - false, + None, false, ) { Ok(sas) => { diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index d460f54b..b472db4a 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -40,6 +40,7 @@ use tracing::trace; use super::{ event_enums::{CancelContent, DoneContent, OutgoingContent, OwnedStartContent, StartContent}, + requests::RequestHandle, Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ @@ -84,6 +85,7 @@ pub struct QrVerification { inner: Arc, state: Arc>, identities: IdentitiesBeingVerified, + request_handle: Option, we_started: bool, } @@ -222,11 +224,15 @@ impl QrVerification { /// /// [`cancel()`]: #method.cancel pub fn cancel_with_code(&self, code: CancelCode) -> Option { + let mut state = self.state.lock().unwrap(); + + if let Some(request) = &self.request_handle { + request.cancel_with_code(&code); + } + let new_state = QrState::::new(true, code); let content = new_state.as_content(self.flow_id()); - let mut state = self.state.lock().unwrap(); - match &*state { InnerState::Confirmed(_) | InnerState::Created(_) @@ -438,6 +444,7 @@ impl QrVerification { other_device_key: String, identities: IdentitiesBeingVerified, we_started: bool, + request_handle: Option, ) -> Self { let secret = Self::generate_secret(); @@ -449,7 +456,7 @@ impl QrVerification { ) .into(); - Self::new_helper(store, flow_id, inner, identities, we_started) + Self::new_helper(store, flow_id, inner, identities, we_started, request_handle) } pub(crate) fn new_self_no_master( @@ -459,6 +466,7 @@ impl QrVerification { own_master_key: String, identities: IdentitiesBeingVerified, we_started: bool, + request_handle: Option, ) -> QrVerification { let secret = Self::generate_secret(); @@ -470,7 +478,7 @@ impl QrVerification { ) .into(); - Self::new_helper(store, flow_id, inner, identities, we_started) + Self::new_helper(store, flow_id, inner, identities, we_started, request_handle) } pub(crate) fn new_cross( @@ -480,6 +488,7 @@ impl QrVerification { other_master_key: String, identities: IdentitiesBeingVerified, we_started: bool, + request_handle: Option, ) -> Self { let secret = Self::generate_secret(); @@ -492,7 +501,7 @@ impl QrVerification { let inner: QrVerificationData = VerificationData::new(event_id, own_master_key, other_master_key, secret).into(); - Self::new_helper(store, flow_id, inner, identities, we_started) + Self::new_helper(store, flow_id, inner, identities, we_started, request_handle) } #[allow(clippy::too_many_arguments)] @@ -505,6 +514,7 @@ impl QrVerification { flow_id: FlowId, qr_code: QrVerificationData, we_started: bool, + request_handle: Option, ) -> Result { if flow_id.as_str() != qr_code.flow_id() { return Err(ScanError::FlowIdMismatch { @@ -602,6 +612,7 @@ impl QrVerification { .into(), identities, we_started, + request_handle, }) } @@ -611,6 +622,7 @@ impl QrVerification { inner: QrVerificationData, identities: IdentitiesBeingVerified, we_started: bool, + request_handle: Option, ) -> Self { let secret = inner.secret().to_owned(); @@ -621,6 +633,7 @@ impl QrVerification { state: Mutex::new(InnerState::Created(QrState { state: Created { secret } })).into(), identities, we_started, + request_handle, } } } @@ -848,6 +861,7 @@ mod test { master_key.clone(), identities.clone(), false, + None, ); assert_eq!(verification.inner.first_key(), &device_key); @@ -860,6 +874,7 @@ mod test { device_key.clone(), identities.clone(), false, + None, ); assert_eq!(verification.inner.first_key(), &master_key); @@ -878,6 +893,7 @@ mod test { bob_master_key.clone(), identities, false, + None, ); assert_eq!(verification.inner.first_key(), &master_key); @@ -922,6 +938,7 @@ mod test { master_key.clone(), identities, false, + None, ); let bob_store = memory_store(); @@ -943,6 +960,7 @@ mod test { flow_id, qr_code, false, + None, ) .await .unwrap(); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 1c661e82..435e7e03 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -71,6 +71,31 @@ pub struct VerificationRequest { we_started: bool, } +/// A handle to a request so child verification flows can cancel the request. +/// +/// A verification flow can branch off into different types of verification +/// flows after the initial request handshake is done. +/// +/// Cancelling a QR code verification should also cancel the request. This +/// `RequestHandle` allows the QR code verification object to cancel the parent +/// `VerificationRequest` object. +#[derive(Clone, Debug)] +pub(crate) struct RequestHandle { + inner: Arc>, +} + +impl RequestHandle { + pub fn cancel_with_code(&self, cancel_code: &CancelCode) { + self.inner.lock().unwrap().cancel(true, cancel_code) + } +} + +impl From>> for RequestHandle { + fn from(inner: Arc>) -> Self { + Self { inner } + } +} + impl VerificationRequest { pub(crate) fn new( cache: VerificationCache, @@ -254,7 +279,11 @@ impl VerificationRequest { /// Generate a QR code that can be used by another client to start a QR code /// based verification. pub async fn generate_qr_code(&self) -> Result, CryptoStoreError> { - self.inner.lock().unwrap().generate_qr_code(self.we_started).await + self.inner + .lock() + .unwrap() + .generate_qr_code(self.we_started, self.inner.clone().into()) + .await } /// Start a QR code verification by providing a scanned QR code for this @@ -280,6 +309,7 @@ impl VerificationRequest { r.flow_id.as_ref().to_owned(), data, self.we_started, + Some(self.inner.clone().into()), ) .await?; @@ -411,7 +441,7 @@ impl VerificationRequest { let inner = self.inner.lock().unwrap().clone(); if let InnerRequest::Ready(s) = inner { - s.receive_start(sender, content, self.we_started).await?; + s.receive_start(sender, content, self.we_started, self.inner.clone().into()).await?; } else { warn!( sender = sender.as_str(), @@ -457,6 +487,7 @@ impl VerificationRequest { s.account.clone(), s.private_cross_signing_identity.clone(), self.we_started, + self.inner.clone().into(), ) .await? { @@ -544,11 +575,12 @@ impl InnerRequest { async fn generate_qr_code( &self, we_started: bool, + request_handle: RequestHandle, ) -> Result, CryptoStoreError> { match self { InnerRequest::Created(_) => Ok(None), InnerRequest::Requested(_) => Ok(None), - InnerRequest::Ready(s) => s.generate_qr_code(we_started).await, + InnerRequest::Ready(s) => s.generate_qr_code(we_started, request_handle).await, InnerRequest::Passive(_) => Ok(None), InnerRequest::Done(_) => Ok(None), InnerRequest::Cancelled(_) => Ok(None), @@ -752,6 +784,7 @@ impl RequestState { other_device: ReadOnlyDevice, other_identity: Option, we_started: bool, + request_handle: RequestHandle, ) -> Result { Sas::from_start_event( (&*self.flow_id).to_owned(), @@ -761,7 +794,7 @@ impl RequestState { self.private_cross_signing_identity.clone(), other_device, other_identity, - true, + Some(request_handle), we_started, ) } @@ -769,6 +802,7 @@ impl RequestState { async fn generate_qr_code( &self, we_started: bool, + request_handle: RequestHandle, ) -> Result, CryptoStoreError> { // If we didn't state that we support showing QR codes or if the other // side doesn't support scanning QR codes bail early. @@ -814,6 +848,7 @@ impl RequestState { device_key.to_owned(), identites, we_started, + Some(request_handle), )) } else { warn!( @@ -832,6 +867,7 @@ impl RequestState { master_key.to_owned(), identites, we_started, + Some(request_handle), )) } } else { @@ -862,6 +898,7 @@ impl RequestState { other_master.to_owned(), identites, we_started, + Some(request_handle), )) } else { warn!( @@ -906,6 +943,7 @@ impl RequestState { sender: &UserId, content: &StartContent<'_>, we_started: bool, + request_handle: RequestHandle, ) -> Result<(), CryptoStoreError> { info!( sender = sender.as_str(), @@ -929,7 +967,13 @@ impl RequestState { match content.method() { StartMethod::SasV1(_) => { - match self.to_started_sas(content, device.clone(), identity, we_started) { + match self.to_started_sas( + content, + device.clone(), + identity, + we_started, + request_handle, + ) { // TODO check if there is already a SAS verification, i.e. we // already started one before the other side tried to do the // same; ignore it if we did and we're the lexicographically @@ -982,6 +1026,7 @@ impl RequestState { account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, we_started: bool, + request_handle: RequestHandle, ) -> Result, CryptoStoreError> { if !self.state.their_methods.contains(&VerificationMethod::SasV1) { return Ok(None); @@ -1027,6 +1072,7 @@ impl RequestState { store, other_identity, we_started, + request_handle, ); (sas, content) } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index bdf1f107..c0cb4fe4 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -38,6 +38,7 @@ use tracing::trace; use super::{ event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent}, + requests::RequestHandle, FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ @@ -56,6 +57,7 @@ pub struct Sas { identities_being_verified: IdentitiesBeingVerified, flow_id: Arc, we_started: bool, + request_handle: Option, } impl Sas { @@ -145,6 +147,7 @@ impl Sas { self.inner.lock().unwrap().set_creation_time(time) } + #[allow(clippy::too_many_arguments)] fn start_helper( inner_sas: InnerSas, account: ReadOnlyAccount, @@ -153,6 +156,7 @@ impl Sas { store: Arc, other_identity: Option, we_started: bool, + request_handle: Option, ) -> Sas { let flow_id = inner_sas.verification_flow_id(); @@ -169,6 +173,7 @@ impl Sas { identities_being_verified: identities, flow_id, we_started, + request_handle, } } @@ -207,6 +212,7 @@ impl Sas { store, other_identity, we_started, + None, ), content, ) @@ -232,6 +238,7 @@ impl Sas { store: Arc, other_identity: Option, we_started: bool, + request_handle: RequestHandle, ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start_in_room( flow_id, @@ -250,6 +257,7 @@ impl Sas { store, other_identity, we_started, + Some(request_handle), ), content, ) @@ -274,7 +282,7 @@ impl Sas { private_identity: PrivateCrossSigningIdentity, other_device: ReadOnlyDevice, other_identity: Option, - started_from_request: bool, + request_handle: Option, we_started: bool, ) -> Result { let inner = InnerSas::from_start_event( @@ -283,7 +291,7 @@ impl Sas { flow_id, content, other_identity.clone(), - started_from_request, + request_handle.is_some(), )?; Ok(Self::start_helper( @@ -294,6 +302,7 @@ impl Sas { store, other_identity, we_started, + request_handle, )) } @@ -418,6 +427,11 @@ impl Sas { /// [`cancel()`]: #method.cancel pub fn cancel_with_code(&self, code: CancelCode) -> Option { let mut guard = self.inner.lock().unwrap(); + + if let Some(request) = &self.request_handle { + request.cancel_with_code(&code) + } + let sas: InnerSas = (*guard).clone(); let (sas, content) = sas.cancel(true, code); *guard = sas; @@ -626,7 +640,7 @@ mod test { PrivateCrossSigningIdentity::empty(bob_id()), alice_device, None, - false, + None, false, ) .unwrap(); From 7644ceea8a0fd936f6a2830390300bcfb75934a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 14:31:54 +0200 Subject: [PATCH 69/88] crypto: Make sure we don't deadlock when we cancel the verification request --- .../src/verification/requests.rs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 435e7e03..7e4fa6df 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -385,15 +385,6 @@ impl VerificationRequest { /// Cancel the verification request pub fn cancel(&self) -> Option { - if let Some(verification) = - self.verification_cache.get(self.other_user(), self.flow_id().as_str()) - { - match verification { - crate::Verification::SasV1(s) => s.cancel(), - crate::Verification::QrV1(q) => q.cancel(), - }; - } - let mut inner = self.inner.lock().unwrap(); inner.cancel(true, &CancelCode::User); @@ -403,14 +394,27 @@ impl VerificationRequest { None }; - content.map(|c| match c { + let request = content.map(|c| match c { OutgoingContent::ToDevice(content) => { ToDeviceRequest::new(&self.other_user(), inner.other_device_id(), content).into() } OutgoingContent::Room(room_id, content) => { RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into() } - }) + }); + + drop(inner); + + if let Some(verification) = + self.verification_cache.get(self.other_user(), self.flow_id().as_str()) + { + match verification { + crate::Verification::SasV1(s) => s.cancel(), + crate::Verification::QrV1(q) => q.cancel(), + }; + } + + request } pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) { From cca73b26225af4928ee156e9872c1be56cf73fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 16:13:30 +0200 Subject: [PATCH 70/88] crypto: Update the SAS event timeout when we receive events --- .../src/verification/sas/sas_state.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index f4e77c2a..06b59a7e 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -466,7 +466,7 @@ impl SasState { ids: self.ids, verification_flow_id: self.verification_flow_id, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), started_from_request: self.started_from_request, state: Arc::new(Accepted { start_content, @@ -642,7 +642,7 @@ impl SasState { ids: self.ids, verification_flow_id: self.verification_flow_id, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), started_from_request: self.started_from_request, state: Arc::new(KeyReceived { we_started: false, @@ -690,7 +690,7 @@ impl SasState { ids: self.ids, verification_flow_id: self.verification_flow_id, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), started_from_request: self.started_from_request, state: Arc::new(KeyReceived { their_pubkey, @@ -819,7 +819,7 @@ impl SasState { inner: self.inner, verification_flow_id: self.verification_flow_id, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), ids: self.ids, started_from_request: self.started_from_request, state: Arc::new(MacReceived { @@ -878,7 +878,7 @@ impl SasState { Ok(SasState { inner: self.inner, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), verification_flow_id: self.verification_flow_id, started_from_request: self.started_from_request, ids: self.ids, @@ -918,7 +918,7 @@ impl SasState { Ok(SasState { inner: self.inner, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), verification_flow_id: self.verification_flow_id, started_from_request: self.started_from_request, ids: self.ids, @@ -1064,7 +1064,7 @@ impl SasState { Ok(SasState { inner: self.inner, creation_time: self.creation_time, - last_event_time: self.last_event_time, + last_event_time: Instant::now().into(), verification_flow_id: self.verification_flow_id, started_from_request: self.started_from_request, ids: self.ids, From 71c89c26701095a3f4de7582dd9bf34d9d7d83ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 17:01:35 +0200 Subject: [PATCH 71/88] crypto: Time out verification requests as well --- matrix_sdk_crypto/src/verification/cache.rs | 22 ++++++--- .../src/verification/event_enums.rs | 15 +++--- matrix_sdk_crypto/src/verification/machine.rs | 16 +++++- matrix_sdk_crypto/src/verification/mod.rs | 5 +- matrix_sdk_crypto/src/verification/qrcode.rs | 12 ++--- .../src/verification/requests.rs | 49 ++++++++++++++----- 6 files changed, 84 insertions(+), 35 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/cache.rs b/matrix_sdk_crypto/src/verification/cache.rs index 5d029634..2124969b 100644 --- a/matrix_sdk_crypto/src/verification/cache.rs +++ b/matrix_sdk_crypto/src/verification/cache.rs @@ -17,9 +17,13 @@ use std::sync::Arc; use dashmap::DashMap; use matrix_sdk_common::uuid::Uuid; use ruma::{DeviceId, UserId}; +use tracing::trace; use super::{event_enums::OutgoingContent, Sas, Verification}; -use crate::{OutgoingRequest, QrVerification, RoomMessageRequest, ToDeviceRequest}; +use crate::{ + OutgoingRequest, OutgoingVerificationRequest, QrVerification, RoomMessageRequest, + ToDeviceRequest, +}; #[derive(Clone, Debug)] pub struct VerificationCache { @@ -73,7 +77,7 @@ impl VerificationCache { self.outgoing_requests.iter().map(|r| (*r).clone()).collect() } - pub fn garbage_collect(&self) -> Vec { + pub fn garbage_collect(&self) -> Vec { for user_verification in self.verification.iter() { user_verification.retain(|_, s| !(s.is_done() || s.is_cancelled())); } @@ -83,15 +87,12 @@ impl VerificationCache { self.verification .iter() .flat_map(|v| { - let requests: Vec = v + let requests: Vec = v .value() .iter() .filter_map(|s| { if let Verification::SasV1(s) = s.value() { - s.cancel_if_timed_out().map(|r| OutgoingRequest { - request_id: r.request_id(), - request: Arc::new(r.into()), - }) + s.cancel_if_timed_out() } else { None } @@ -114,9 +115,16 @@ impl VerificationCache { } pub fn add_request(&self, request: OutgoingRequest) { + trace!("Adding an outgoing verification request {:?}", request); self.outgoing_requests.insert(request.request_id, request); } + pub fn add_verification_request(&self, request: OutgoingVerificationRequest) { + let request = + OutgoingRequest { request_id: request.request_id(), request: Arc::new(request.into()) }; + self.add_request(request); + } + pub fn queue_up_content( &self, recipient: &UserId, diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index 9c63f540..e7c66725 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -665,16 +665,15 @@ impl From<(RoomId, AnyMessageEventContent)> for OutgoingContent { } } -#[cfg(test)] -use crate::OutgoingVerificationRequest; -use crate::{OutgoingRequest, RoomMessageRequest, ToDeviceRequest}; +use crate::{OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest}; -#[cfg(test)] -impl From for OutgoingContent { - fn from(request: OutgoingVerificationRequest) -> Self { +impl TryFrom for OutgoingContent { + type Error = String; + + fn try_from(request: OutgoingVerificationRequest) -> Result { match request { - OutgoingVerificationRequest::ToDevice(r) => Self::try_from(r).unwrap(), - OutgoingVerificationRequest::InRoom(r) => Self::from(r), + OutgoingVerificationRequest::ToDevice(r) => Self::try_from(r), + OutgoingVerificationRequest::InRoom(r) => Ok(Self::from(r)), } } } diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 3a89b070..3bea8480 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -247,7 +247,19 @@ impl VerificationMachine { } self.requests.retain(|_, v| !v.is_empty()); - for request in self.verifications.garbage_collect() { + let mut requests: Vec = self + .requests + .iter() + .flat_map(|v| { + let requests: Vec = + v.value().iter().filter_map(|v| v.cancel_if_timed_out()).collect(); + requests + }) + .collect(); + + requests.extend(self.verifications.garbage_collect().into_iter()); + + for request in requests { if let Ok(OutgoingContent::ToDevice(AnyToDeviceEventContent::KeyVerificationCancel( content, ))) = request.clone().try_into() @@ -257,7 +269,7 @@ impl VerificationMachine { events.push(AnyToDeviceEvent::KeyVerificationCancel(event).into()); } - self.verifications.add_request(request) + self.verifications.add_verification_request(request) } events diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 025fca1e..49ae8e70 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -525,6 +525,8 @@ impl IdentitiesBeingVerified { #[cfg(test)] pub(crate) mod test { + use std::convert::TryInto; + use ruma::{ events::{AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent}, UserId, @@ -540,7 +542,8 @@ pub(crate) mod test { sender: &UserId, request: &OutgoingVerificationRequest, ) -> AnyToDeviceEvent { - let content = request.to_owned().into(); + let content = + request.to_owned().try_into().expect("Can't fetch content out of the request"); wrap_any_to_device_content(sender, content) } diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index b472db4a..baff6fab 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -207,7 +207,7 @@ impl QrVerification { /// Cancel the verification flow. pub fn cancel(&self) -> Option { - self.cancel_with_code(CancelCode::User).map(|c| self.content_to_request(c)) + self.cancel_with_code(CancelCode::User) } /// Cancel the verification. @@ -223,7 +223,7 @@ impl QrVerification { /// otherwise it returns a request that needs to be sent out. /// /// [`cancel()`]: #method.cancel - pub fn cancel_with_code(&self, code: CancelCode) -> Option { + pub fn cancel_with_code(&self, code: CancelCode) -> Option { let mut state = self.state.lock().unwrap(); if let Some(request) = &self.request_handle { @@ -240,7 +240,7 @@ impl QrVerification { | InnerState::Reciprocated(_) | InnerState::Done(_) => { *state = InnerState::Cancelled(new_state); - Some(content) + Some(self.content_to_request(content)) } InnerState::Cancelled(_) => None, } @@ -966,20 +966,20 @@ mod test { .unwrap(); let request = bob_verification.reciprocate().unwrap(); - let content = OutgoingContent::from(request); + let content = OutgoingContent::try_from(request).unwrap(); let content = StartContent::try_from(&content).unwrap(); alice_verification.receive_reciprocation(&content); let request = alice_verification.confirm_scanning().unwrap(); - let content = OutgoingContent::from(request); + let content = OutgoingContent::try_from(request).unwrap(); let content = DoneContent::try_from(&content).unwrap(); assert!(!alice_verification.is_done()); assert!(!bob_verification.is_done()); let (request, _) = bob_verification.receive_done(&content).await.unwrap(); - let content = OutgoingContent::from(request.unwrap()); + let content = OutgoingContent::try_from(request.unwrap()).unwrap(); let content = DoneContent::try_from(&content).unwrap(); alice_verification.receive_done(&content).await.unwrap(); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 7e4fa6df..230407d6 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::{Arc, Mutex}; +use std::{ + sync::{Arc, Mutex}, + time::Duration, +}; use matrix_qrcode::QrVerificationData; -use matrix_sdk_common::uuid::Uuid; +use matrix_sdk_common::{instant::Instant, uuid::Uuid}; use ruma::{ events::{ key::verification::{ @@ -54,6 +57,8 @@ const SUPPORTED_METHODS: &[VerificationMethod] = &[ VerificationMethod::ReciprocateV1, ]; +const VERIFICATION_TIMEOUT: Duration = Duration::from_secs(60 * 10); + /// An object controlling key verification requests. /// /// Interactive verification flows usually start with a verification request, @@ -68,6 +73,7 @@ pub struct VerificationRequest { flow_id: Arc, other_user_id: Arc, inner: Arc>, + creation_time: Arc, we_started: bool, } @@ -123,6 +129,7 @@ impl VerificationRequest { flow_id: flow_id.into(), inner, other_user_id: other_user.to_owned().into(), + creation_time: Instant::now().into(), we_started: true, } } @@ -220,6 +227,11 @@ impl VerificationRequest { matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_)) } + /// Has the verification flow timed out. + pub fn timed_out(&self) -> bool { + self.creation_time.elapsed() > VERIFICATION_TIMEOUT + } + /// Get the supported verification methods of the other side. /// /// Will be present only if the other side requested the verification or if @@ -345,6 +357,7 @@ impl VerificationRequest { other_user_id: sender.to_owned().into(), flow_id: flow_id.into(), we_started: false, + creation_time: Instant::now().into(), } } @@ -385,8 +398,12 @@ impl VerificationRequest { /// Cancel the verification request pub fn cancel(&self) -> Option { + self.cancel_with_code(CancelCode::User) + } + + fn cancel_with_code(&self, cancel_code: CancelCode) -> Option { let mut inner = self.inner.lock().unwrap(); - inner.cancel(true, &CancelCode::User); + inner.cancel(true, &cancel_code); let content = if let InnerRequest::Cancelled(c) = &*inner { Some(c.state.as_content(self.flow_id())) @@ -409,14 +426,24 @@ impl VerificationRequest { self.verification_cache.get(self.other_user(), self.flow_id().as_str()) { match verification { - crate::Verification::SasV1(s) => s.cancel(), - crate::Verification::QrV1(q) => q.cancel(), + crate::Verification::SasV1(s) => s.cancel_with_code(cancel_code), + crate::Verification::QrV1(q) => q.cancel_with_code(cancel_code), }; } request } + pub(crate) fn cancel_if_timed_out(&self) -> Option { + if self.is_cancelled() || self.is_done() { + None + } else if self.timed_out() { + self.cancel_with_code(CancelCode::Timeout) + } else { + None + } + } + pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) { let mut inner = self.inner.lock().unwrap(); @@ -1095,7 +1122,7 @@ struct Done {} #[cfg(test)] mod test { - use std::convert::TryFrom; + use std::convert::{TryFrom, TryInto}; use matrix_sdk_test::async_test; use ruma::{event_id, room_id, DeviceIdBox, UserId}; @@ -1166,7 +1193,7 @@ mod test { &(&content).into(), ); - let content: OutgoingContent = alice_request.accept().unwrap().into(); + let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap(); let content = ReadyContent::try_from(&content).unwrap(); bob_request.receive_ready(&alice_id(), &content); @@ -1223,7 +1250,7 @@ mod test { &(&content).into(), ); - let content: OutgoingContent = alice_request.accept().unwrap().into(); + let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap(); let content = ReadyContent::try_from(&content).unwrap(); bob_request.receive_ready(&alice_id(), &content); @@ -1233,7 +1260,7 @@ mod test { let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); - let content: OutgoingContent = request.into(); + 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(); @@ -1290,7 +1317,7 @@ mod test { &(&content).into(), ); - let content: OutgoingContent = alice_request.accept().unwrap().into(); + let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap(); let content = ReadyContent::try_from(&content).unwrap(); bob_request.receive_ready(&alice_id(), &content); @@ -1300,7 +1327,7 @@ mod test { let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap(); - let content: OutgoingContent = request.into(); + 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(); From b53518d1b8dd93ac447d1318c2e8aa4e2004dd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Fri, 9 Jul 2021 17:05:23 +0200 Subject: [PATCH 72/88] crypto: Improve a log line --- matrix_sdk_crypto/src/session_manager/group_sessions.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/matrix_sdk_crypto/src/session_manager/group_sessions.rs b/matrix_sdk_crypto/src/session_manager/group_sessions.rs index f4b210a6..e9b6bd3f 100644 --- a/matrix_sdk_crypto/src/session_manager/group_sessions.rs +++ b/matrix_sdk_crypto/src/session_manager/group_sessions.rs @@ -412,10 +412,7 @@ impl GroupSessionManager { users: impl Iterator, encryption_settings: impl Into, ) -> OlmResult>> { - debug!( - room_id = room_id.as_str(), - "Checking if a group session needs to be shared for room {}", room_id - ); + debug!(room_id = room_id.as_str(), "Checking if a room key needs to be shared",); let encryption_settings = encryption_settings.into(); let history_visibility = encryption_settings.history_visibility.clone(); From ead91a1e6b177d1a99b0a5a3232cd68694ff99b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 12 Jul 2021 18:12:02 +0200 Subject: [PATCH 73/88] crypto: Send cancellations if the other device picks up over to-device --- matrix_sdk_base/src/store/memory_store.rs | 3 +- matrix_sdk_crypto/src/identities/user.rs | 26 ++++- matrix_sdk_crypto/src/requests.rs | 23 ++++ .../src/verification/event_enums.rs | 27 +++++ matrix_sdk_crypto/src/verification/machine.rs | 31 +++--- .../src/verification/requests.rs | 105 ++++++++++++++++-- 6 files changed, 183 insertions(+), 32 deletions(-) diff --git a/matrix_sdk_base/src/store/memory_store.rs b/matrix_sdk_base/src/store/memory_store.rs index 6c919a4b..87d94093 100644 --- a/matrix_sdk_base/src/store/memory_store.rs +++ b/matrix_sdk_base/src/store/memory_store.rs @@ -69,7 +69,7 @@ pub struct MemoryStore { } impl MemoryStore { - #[cfg(not(feature = "sled_state_store"))] + #[allow(dead_code)] pub fn new() -> Self { Self { sync_token: Arc::new(RwLock::new(None)), @@ -558,7 +558,6 @@ impl StateStore for MemoryStore { } #[cfg(test)] -#[cfg(not(feature = "sled_state_store"))] mod test { use matrix_sdk_test::async_test; use ruma::{ diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index c124d4e4..13ca15d6 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -27,7 +27,7 @@ use ruma::{ events::{ key::verification::VerificationMethod, room::message::KeyVerificationRequestEventContent, }, - DeviceKeyId, EventId, RoomId, UserId, + DeviceIdBox, DeviceKeyId, EventId, RoomId, UserId, }; use serde::{Deserialize, Serialize}; use serde_json::to_value; @@ -108,7 +108,7 @@ impl OwnUserIdentity { pub async fn request_verification( &self, ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { - self.verification_machine.request_self_verification(&self.inner, None).await + self.request_verification_helper(None).await } /// Send a verification request to our other devices while specifying our @@ -121,7 +121,27 @@ impl OwnUserIdentity { &self, methods: Vec, ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { - self.verification_machine.request_self_verification(&self.inner, Some(methods)).await + self.request_verification_helper(Some(methods)).await + } + + async fn request_verification_helper( + &self, + methods: Option>, + ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { + let devices: Vec = self + .verification_machine + .store + .get_user_devices(self.user_id()) + .await? + .into_iter() + .map(|(d, _)| d) + .filter(|d| &**d != self.verification_machine.own_device_id()) + .collect(); + + Ok(self + .verification_machine + .request_to_device_verification(self.user_id(), devices, methods) + .await) } } diff --git a/matrix_sdk_crypto/src/requests.rs b/matrix_sdk_crypto/src/requests.rs index 428edaef..79981b26 100644 --- a/matrix_sdk_crypto/src/requests.rs +++ b/matrix_sdk_crypto/src/requests.rs @@ -78,6 +78,29 @@ impl ToDeviceRequest { Self::new_with_id(recipient, recipient_device, content, Uuid::new_v4()) } + pub(crate) fn new_for_recipients( + recipient: &UserId, + recipient_devices: Vec, + content: AnyToDeviceEventContent, + txn_id: Uuid, + ) -> Self { + let mut messages = BTreeMap::new(); + let event_type = EventType::from(content.event_type()); + + if recipient_devices.is_empty() { + Self::new(recipient, DeviceIdOrAllDevices::AllDevices, content) + } else { + let device_messages = recipient_devices + .into_iter() + .map(|d| (DeviceIdOrAllDevices::DeviceId(d), Raw::from(content.clone()))) + .collect(); + + messages.insert(recipient.clone(), device_messages); + + ToDeviceRequest { event_type, txn_id, messages } + } + } + pub(crate) fn new_with_id( recipient: &UserId, recipient_device: impl Into, diff --git a/matrix_sdk_crypto/src/verification/event_enums.rs b/matrix_sdk_crypto/src/verification/event_enums.rs index e7c66725..0a01c8ad 100644 --- a/matrix_sdk_crypto/src/verification/event_enums.rs +++ b/matrix_sdk_crypto/src/verification/event_enums.rs @@ -379,6 +379,33 @@ try_from_outgoing_content!(MacContent, KeyVerificationMac); try_from_outgoing_content!(CancelContent, KeyVerificationCancel); try_from_outgoing_content!(DoneContent, KeyVerificationDone); +impl<'a> TryFrom<&'a OutgoingContent> for RequestContent<'a> { + type Error = (); + + fn try_from(value: &'a OutgoingContent) -> Result { + match value { + OutgoingContent::Room(_, c) => { + if let AnyMessageEventContent::RoomMessage(m) = c { + if let MessageType::VerificationRequest(c) = &m.msgtype { + Ok(Self::Room(c)) + } else { + Err(()) + } + } else { + Err(()) + } + } + OutgoingContent::ToDevice(c) => { + if let AnyToDeviceEventContent::KeyVerificationRequest(c) = c { + Ok(Self::ToDevice(c)) + } else { + Err(()) + } + } + } + } +} + #[derive(Debug)] pub enum StartContent<'a> { ToDevice(&'a StartToDeviceEventContent), diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 3bea8480..8d0c4858 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -25,8 +25,7 @@ use ruma::{ ToDeviceEvent, }, serde::Raw, - to_device::DeviceIdOrAllDevices, - DeviceId, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, + DeviceId, DeviceIdBox, EventId, MilliSecondsSinceUnixEpoch, RoomId, UserId, }; use tracing::{info, trace, warn}; @@ -41,8 +40,8 @@ use crate::{ olm::PrivateCrossSigningIdentity, requests::OutgoingRequest, store::{CryptoStore, CryptoStoreError}, - OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity, - ReadOnlyUserIdentity, RoomMessageRequest, ToDeviceRequest, + OutgoingVerificationRequest, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyUserIdentity, + RoomMessageRequest, ToDeviceRequest, }; #[derive(Clone, Debug)] @@ -77,11 +76,12 @@ impl VerificationMachine { self.account.device_id() } - pub(crate) async fn request_self_verification( + pub(crate) async fn request_to_device_verification( &self, - identity: &ReadOnlyOwnUserIdentity, + user_id: &UserId, + recipient_devices: Vec, methods: Option>, - ) -> Result<(VerificationRequest, OutgoingVerificationRequest), CryptoStoreError> { + ) -> (VerificationRequest, OutgoingVerificationRequest) { let flow_id = FlowId::from(Uuid::new_v4().to_string()); let verification = VerificationRequest::new( @@ -90,22 +90,16 @@ impl VerificationMachine { self.private_identity.lock().await.clone(), self.store.clone(), flow_id, - identity.user_id(), + user_id, + recipient_devices, methods, ); - // TODO get all the device ids of the user instead of using AllDevices - // make sure to remember this so we can cancel once someone picks up - let request: OutgoingVerificationRequest = ToDeviceRequest::new( - identity.user_id(), - DeviceIdOrAllDevices::AllDevices, - AnyToDeviceEventContent::KeyVerificationRequest(verification.request_to_device()), - ) - .into(); - self.insert_request(verification.clone()); - Ok((verification, request)) + let request = verification.request_to_device(); + + (verification, request.into()) } pub async fn request_verification( @@ -124,6 +118,7 @@ impl VerificationMachine { self.store.clone(), flow_id, identity.user_id(), + vec![], methods, ); diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 230407d6..450dfeb0 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -75,6 +75,7 @@ pub struct VerificationRequest { inner: Arc>, creation_time: Arc, we_started: bool, + recipient_devices: Arc>, } /// A handle to a request so child verification flows can cancel the request. @@ -110,6 +111,7 @@ impl VerificationRequest { store: Arc, flow_id: FlowId, other_user: &UserId, + recipient_devices: Vec, methods: Option>, ) -> Self { let inner = Mutex::new(InnerRequest::Created(RequestState::new( @@ -131,6 +133,7 @@ impl VerificationRequest { other_user_id: other_user.to_owned().into(), creation_time: Instant::now().into(), we_started: true, + recipient_devices: recipient_devices.into(), } } @@ -138,7 +141,7 @@ impl VerificationRequest { /// verification from the other side. This should be used only for /// self-verifications and it should be sent to the specific device that we /// want to verify. - pub(crate) fn request_to_device(&self) -> RequestToDeviceEventContent { + pub(crate) fn request_to_device(&self) -> ToDeviceRequest { let inner = self.inner.lock().unwrap(); let methods = if let InnerRequest::Created(c) = &*inner { @@ -147,11 +150,18 @@ impl VerificationRequest { SUPPORTED_METHODS.to_vec() }; - RequestToDeviceEventContent::new( + let content = RequestToDeviceEventContent::new( self.account.device_id().into(), self.flow_id().as_str().to_string(), methods, MilliSecondsSinceUnixEpoch::now(), + ); + + ToDeviceRequest::new_for_recipients( + self.other_user(), + self.recipient_devices.to_vec(), + AnyToDeviceEventContent::KeyVerificationRequest(content), + Uuid::new_v4(), ) } @@ -358,6 +368,7 @@ impl VerificationRequest { flow_id: flow_id.into(), we_started: false, creation_time: Instant::now().into(), + recipient_devices: vec![].into(), } } @@ -403,6 +414,10 @@ impl VerificationRequest { fn cancel_with_code(&self, cancel_code: CancelCode) -> Option { let mut inner = self.inner.lock().unwrap(); + + let send_to_everyone = self.we_started() && matches!(&*inner, InnerRequest::Created(_)); + let other_device = inner.other_device_id(); + inner.cancel(true, &cancel_code); let content = if let InnerRequest::Cancelled(c) = &*inner { @@ -413,7 +428,17 @@ impl VerificationRequest { let request = content.map(|c| match c { OutgoingContent::ToDevice(content) => { - ToDeviceRequest::new(&self.other_user(), inner.other_device_id(), content).into() + if send_to_everyone { + ToDeviceRequest::new_for_recipients( + &self.other_user(), + self.recipient_devices.to_vec(), + content, + Uuid::new_v4(), + ) + .into() + } else { + ToDeviceRequest::new(&self.other_user(), other_device, content).into() + } } OutgoingContent::Room(room_id, content) => { RoomMessageRequest { room_id, txn_id: Uuid::new_v4(), content }.into() @@ -444,12 +469,65 @@ impl VerificationRequest { } } + /// Create a key verification cancellation for devices that received the + /// request but either shouldn't continue in the verification or didn't get + /// notified that the other side cancelled. + /// + /// The spec states the following[1]: + /// When Bob accepts or declines the verification on one of his devices + /// (sending either an m.key.verification.ready or m.key.verification.cancel + /// event), Alice will send an m.key.verification.cancel event to Bob’s + /// other devices with a code of m.accepted in the case where Bob accepted + /// the verification, or m.user in the case where Bob rejected the + /// verification. + /// + /// Realistically sending the cancellation to Bob's other devices is only + /// possible if Bob accepted the verification since we don't know the device + /// id of Bob's device that rejected the verification. + /// + /// Thus, we're sending the cancellation to all devices that received the + /// request in the rejection case. + /// + /// [1]: https://spec.matrix.org/unstable/client-server-api/#key-verification-framework + pub(crate) fn cancel_for_other_devices( + &self, + code: CancelCode, + filter_device: Option<&DeviceId>, + ) -> Option { + let cancelled = Cancelled::new(true, code); + let cancel_content = cancelled.as_content(self.flow_id()); + + if let OutgoingContent::ToDevice(c) = cancel_content { + let recipients: Vec = self + .recipient_devices + .to_vec() + .into_iter() + .filter(|d| if let Some(device) = filter_device { &**d != device } else { true }) + .collect(); + + Some(ToDeviceRequest::new_for_recipients( + self.other_user(), + recipients, + c, + Uuid::new_v4(), + )) + } else { + None + } + } + pub(crate) fn receive_ready(&self, sender: &UserId, content: &ReadyContent) { let mut inner = self.inner.lock().unwrap(); match &*inner { InnerRequest::Created(s) => { *inner = InnerRequest::Ready(s.clone().into_ready(sender, content)); + + if let Some(request) = + self.cancel_for_other_devices(CancelCode::Accepted, Some(content.from_device())) + { + self.verification_cache.add_verification_request(request.into()); + } } InnerRequest::Requested(s) => { if sender == self.own_user_id() && content.from_device() != self.account.device_id() @@ -500,6 +578,12 @@ impl VerificationRequest { ); let mut inner = self.inner.lock().unwrap(); inner.cancel(false, content.cancel_code()); + + if let Some(request) = + self.cancel_for_other_devices(content.cancel_code().to_owned(), None) + { + self.verification_cache.add_verification_request(request.into()); + } } } @@ -597,9 +681,7 @@ impl InnerRequest { InnerRequest::Created(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), InnerRequest::Requested(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), InnerRequest::Ready(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), - InnerRequest::Passive(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), - InnerRequest::Done(_) => return, - InnerRequest::Cancelled(_) => return, + InnerRequest::Passive(_) | InnerRequest::Done(_) | InnerRequest::Cancelled(_) => return, }); } @@ -1133,7 +1215,7 @@ mod test { store::{Changes, CryptoStore, MemoryStore}, verification::{ cache::VerificationCache, - event_enums::{OutgoingContent, ReadyContent, StartContent}, + event_enums::{OutgoingContent, ReadyContent, RequestContent, StartContent}, FlowId, }, ReadOnlyDevice, @@ -1180,6 +1262,7 @@ mod test { bob_store.into(), flow_id.clone(), &alice_id(), + vec![], None, ); @@ -1237,6 +1320,7 @@ mod test { bob_store.into(), flow_id.clone(), &alice_id(), + vec![], None, ); @@ -1301,10 +1385,13 @@ mod test { bob_store.into(), flow_id, &alice_id(), + vec![], None, ); - let content = bob_request.request_to_device(); + let request = bob_request.request_to_device(); + let content: OutgoingContent = request.try_into().unwrap(); + let content = RequestContent::try_from(&content).unwrap(); let flow_id = bob_request.flow_id().to_owned(); let alice_request = VerificationRequest::from_request( @@ -1314,7 +1401,7 @@ mod test { alice_store.into(), &bob_id(), flow_id, - &(&content).into(), + &content, ); let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap(); From 7433003ffa3b8943df83964714b3d62f4b78e689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:21:28 +0200 Subject: [PATCH 74/88] matrix-sdk: Don't panic when importing invalid key exports --- matrix_sdk/src/client.rs | 12 ++++++++---- matrix_sdk/src/error.rs | 31 ++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 3d1d2713..f2105751 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -129,6 +129,7 @@ use ruma::{ #[cfg(feature = "encryption")] use crate::{ device::{Device, UserDevices}, + error::RoomKeyImportError, verification::{QrVerification, SasVerification, Verification, VerificationRequest}, }; use crate::{ @@ -2491,8 +2492,12 @@ impl Client { /// ``` #[cfg(all(feature = "encryption", not(target_arch = "wasm32")))] #[cfg_attr(feature = "docs", doc(cfg(all(encryption, not(target_arch = "wasm32")))))] - pub async fn import_keys(&self, path: PathBuf, passphrase: &str) -> Result<(usize, usize)> { - let olm = self.base_client.olm_machine().await.ok_or(Error::AuthenticationRequired)?; + pub async fn import_keys( + &self, + path: PathBuf, + passphrase: &str, + ) -> StdResult<(usize, usize), RoomKeyImportError> { + let olm = self.base_client.olm_machine().await.ok_or(RoomKeyImportError::StoreClosed)?; let passphrase = Zeroizing::new(passphrase.to_owned()); let decrypt = move || { @@ -2501,8 +2506,7 @@ impl Client { }; let task = tokio::task::spawn_blocking(decrypt); - // TODO remove this unwrap. - let import = task.await.expect("Task join error").unwrap(); + let import = task.await.expect("Task join error")?; Ok(olm.import_keys(import, |_, _| {}).await?) } diff --git a/matrix_sdk/src/error.rs b/matrix_sdk/src/error.rs index 968a9609..2fe22129 100644 --- a/matrix_sdk/src/error.rs +++ b/matrix_sdk/src/error.rs @@ -18,7 +18,9 @@ use std::io::Error as IoError; use http::StatusCode; #[cfg(feature = "encryption")] -use matrix_sdk_base::crypto::{CryptoStoreError, DecryptorError, MegolmError, OlmError}; +use matrix_sdk_base::crypto::{ + CryptoStoreError, DecryptorError, KeyExportError, MegolmError, OlmError, +}; use matrix_sdk_base::{Error as SdkBaseError, StoreError}; use reqwest::Error as ReqwestError; use ruma::{ @@ -151,6 +153,33 @@ pub enum Error { Url(#[from] UrlParseError), } +/// Error for the room key importing functionality. +#[cfg(feature = "encryption")] +#[cfg_attr(feature = "docs", doc(cfg(encryption)))] +#[derive(Error, Debug)] +pub enum RoomKeyImportError { + /// An error de/serializing type for the `StateStore` + #[error(transparent)] + SerdeJson(#[from] JsonError), + + /// The cryptostore isn't yet open, logging in is required to open the + /// cryptostore. + #[error("The cryptostore hasn't been yet opened, can't import yet.")] + StoreClosed, + + /// An IO error happened. + #[error(transparent)] + Io(#[from] IoError), + + /// An error occurred in the crypto store. + #[error(transparent)] + CryptoStore(#[from] CryptoStoreError), + + /// An error occurred while importing the key export. + #[error(transparent)] + Export(#[from] KeyExportError), +} + impl Error { /// Try to destructure the error into an universal interactive auth info. /// From 909cd42ac196a57d10a65db4d4477c196e515d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:32:48 +0200 Subject: [PATCH 75/88] crypto: Cancel the verification request for to-device Sas verifications --- matrix_sdk_crypto/src/identities/device.rs | 2 +- matrix_sdk_crypto/src/verification/machine.rs | 2 ++ matrix_sdk_crypto/src/verification/requests.rs | 1 + matrix_sdk_crypto/src/verification/sas/mod.rs | 4 +++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 79884c84..9b432e77 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -43,7 +43,7 @@ use crate::{ olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session, Utility}, store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult}, verification::VerificationMachine, - OutgoingVerificationRequest, Sas, ToDeviceRequest, VerificationRequest, + OutgoingVerificationRequest, Sas, ToDeviceRequest, }; #[cfg(test)] use crate::{OlmMachine, ReadOnlyAccount}; diff --git a/matrix_sdk_crypto/src/verification/machine.rs b/matrix_sdk_crypto/src/verification/machine.rs index 8d0c4858..fbab484f 100644 --- a/matrix_sdk_crypto/src/verification/machine.rs +++ b/matrix_sdk_crypto/src/verification/machine.rs @@ -142,6 +142,7 @@ impl VerificationMachine { identity, None, true, + None, ); let request = match content { @@ -565,6 +566,7 @@ mod test { None, None, true, + None, ); machine diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 450dfeb0..2c78604c 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -1172,6 +1172,7 @@ impl RequestState { other_identity, Some(t.to_owned()), we_started, + Some(request_handle), ); (sas, content) } diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index c0cb4fe4..fd1e5e24 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -195,6 +195,7 @@ impl Sas { other_identity: Option, transaction_id: Option, we_started: bool, + request_handle: Option, ) -> (Sas, OutgoingContent) { let (inner, content) = InnerSas::start( account.clone(), @@ -212,7 +213,7 @@ impl Sas { store, other_identity, we_started, - None, + request_handle, ), content, ) @@ -627,6 +628,7 @@ mod test { None, None, true, + None, ); let flow_id = alice.flow_id().to_owned(); From ff8089912e95295ed2a8c9c94c8431981d18ee22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:36:21 +0200 Subject: [PATCH 76/88] crypto: Only send cancellations to other devices if we're the requester --- matrix_sdk_crypto/src/verification/requests.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 2c78604c..087728b6 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -579,10 +579,12 @@ impl VerificationRequest { let mut inner = self.inner.lock().unwrap(); inner.cancel(false, content.cancel_code()); - if let Some(request) = - self.cancel_for_other_devices(content.cancel_code().to_owned(), None) - { - self.verification_cache.add_verification_request(request.into()); + if self.we_started() { + if let Some(request) = + self.cancel_for_other_devices(content.cancel_code().to_owned(), None) + { + self.verification_cache.add_verification_request(request.into()); + } } } } From cf30c42563ccf3d6ed4ddd3cb3a95243c002b7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:43:35 +0200 Subject: [PATCH 77/88] crypto: Correctly remember our chosen SAS methods --- .../src/verification/sas/inner_sas.rs | 7 +++-- matrix_sdk_crypto/src/verification/sas/mod.rs | 27 +++---------------- .../src/verification/sas/sas_state.rs | 15 ++++++++--- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 04267442..161181b6 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -165,9 +165,12 @@ impl InnerSas { } } - pub fn accept(self) -> Option<(InnerSas, OwnedAcceptContent)> { + pub fn accept( + self, + methods: Vec, + ) -> Option<(InnerSas, OwnedAcceptContent)> { if let InnerSas::Started(s) = self { - let sas = s.into_accepted(); + let sas = s.into_accepted(methods); let content = sas.as_content(); Some((InnerSas::WeAccepted(sas), content)) } else { diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index fd1e5e24..56159d94 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -25,11 +25,7 @@ use matrix_sdk_common::uuid::Uuid; use ruma::{ api::client::r0::keys::upload_signatures::Request as SignatureUploadRequest, events::{ - key::verification::{ - accept::{AcceptEventContent, AcceptMethod, AcceptToDeviceEventContent}, - cancel::CancelCode, - ShortAuthenticationString, - }, + key::verification::{cancel::CancelCode, ShortAuthenticationString}, AnyMessageEventContent, AnyToDeviceEventContent, }, DeviceId, EventId, RoomId, UserId, @@ -327,10 +323,10 @@ impl Sas { ) -> Option { let mut guard = self.inner.lock().unwrap(); let sas: InnerSas = (*guard).clone(); + let methods = settings.allowed_methods; - if let Some((sas, content)) = sas.accept() { + if let Some((sas, content)) = sas.accept(methods) { *guard = sas; - let content = settings.apply(content); Some(match content { OwnedAcceptContent::ToDevice(c) => { @@ -554,23 +550,6 @@ impl AcceptSettings { pub fn with_allowed_methods(methods: Vec) -> Self { Self { allowed_methods: methods } } - - fn apply(self, mut content: OwnedAcceptContent) -> OwnedAcceptContent { - match &mut content { - OwnedAcceptContent::ToDevice(AcceptToDeviceEventContent { - method: AcceptMethod::SasV1(c), - .. - }) - | OwnedAcceptContent::Room( - _, - AcceptEventContent { method: AcceptMethod::SasV1(c), .. }, - ) => { - c.short_authentication_string.retain(|sas| self.allowed_methods.contains(sas)); - content - } - _ => content, - } - } } #[cfg(test)] diff --git a/matrix_sdk_crypto/src/verification/sas/sas_state.rs b/matrix_sdk_crypto/src/verification/sas/sas_state.rs index 06b59a7e..7aa9bc2f 100644 --- a/matrix_sdk_crypto/src/verification/sas/sas_state.rs +++ b/matrix_sdk_crypto/src/verification/sas/sas_state.rs @@ -557,7 +557,15 @@ impl SasState { } } - pub fn into_accepted(self) -> SasState { + pub fn into_accepted(self, methods: Vec) -> SasState { + let mut accepted_protocols = self.state.accepted_protocols.as_ref().to_owned(); + accepted_protocols.short_auth_string = methods; + + // Decimal is required per spec. + if !accepted_protocols.short_auth_string.contains(&ShortAuthenticationString::Decimal) { + accepted_protocols.short_auth_string.push(ShortAuthenticationString::Decimal); + } + SasState { inner: self.inner, ids: self.ids, @@ -567,7 +575,7 @@ impl SasState { started_from_request: self.started_from_request, state: Arc::new(WeAccepted { we_started: false, - accepted_protocols: self.state.accepted_protocols.clone(), + accepted_protocols: accepted_protocols.into(), commitment: self.state.commitment.clone(), }), } @@ -1115,6 +1123,7 @@ mod test { events::key::verification::{ accept::{AcceptMethod, AcceptToDeviceEventContent}, start::{StartMethod, StartToDeviceEventContent}, + ShortAuthenticationString, }, DeviceId, UserId, }; @@ -1162,7 +1171,7 @@ mod test { &start_content.as_start_content(), false, ); - let bob_sas = bob_sas.unwrap().into_accepted(); + let bob_sas = bob_sas.unwrap().into_accepted(vec![ShortAuthenticationString::Emoji]); (alice_sas, bob_sas) } From 55a9e6836df65061b36a5bc950e792a306e5489e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:45:47 +0200 Subject: [PATCH 78/88] crypto: Introduce a CancelInfo struct This replaces the separate methods to fetch info about the cancellation. It was a bit annoying to gather all the different info where each method can return None. --- matrix_sdk_crypto/src/lib.rs | 4 ++- matrix_sdk_crypto/src/verification/mod.rs | 32 +++++++++++++++++++ matrix_sdk_crypto/src/verification/qrcode.rs | 18 +++-------- .../src/verification/requests.rs | 14 ++++---- .../src/verification/sas/inner_sas.rs | 16 ---------- matrix_sdk_crypto/src/verification/sas/mod.rs | 18 +++++------ 6 files changed, 57 insertions(+), 45 deletions(-) diff --git a/matrix_sdk_crypto/src/lib.rs b/matrix_sdk_crypto/src/lib.rs index a3d7a244..37251fda 100644 --- a/matrix_sdk_crypto/src/lib.rs +++ b/matrix_sdk_crypto/src/lib.rs @@ -57,4 +57,6 @@ pub use requests::{ OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest, }; pub use store::CryptoStoreError; -pub use verification::{AcceptSettings, QrVerification, Sas, Verification, VerificationRequest}; +pub use verification::{ + AcceptSettings, CancelInfo, QrVerification, Sas, Verification, VerificationRequest, +}; diff --git a/matrix_sdk_crypto/src/verification/mod.rs b/matrix_sdk_crypto/src/verification/mod.rs index 49ae8e70..20ed6105 100644 --- a/matrix_sdk_crypto/src/verification/mod.rs +++ b/matrix_sdk_crypto/src/verification/mod.rs @@ -165,6 +165,38 @@ impl Done { } } +/// Information about the cancellation of a verification request or verification +/// flow. +#[derive(Clone, Debug)] +pub struct CancelInfo { + cancelled_by_us: bool, + cancel_code: CancelCode, + reason: &'static str, +} + +impl CancelInfo { + /// Get the human readable reason of the cancellation. + pub fn reason(&self) -> &'static str { + &self.reason + } + + /// Get the `CancelCode` that cancelled this verification. + pub fn cancel_code(&self) -> &CancelCode { + &self.cancel_code + } + + /// Was the verification cancelled by us? + pub fn cancelled_by_us(&self) -> bool { + self.cancelled_by_us + } +} + +impl From for CancelInfo { + fn from(c: Cancelled) -> Self { + Self { cancelled_by_us: c.cancelled_by_us, cancel_code: c.cancel_code, reason: c.reason } + } +} + #[derive(Clone, Debug)] pub struct Cancelled { cancelled_by_us: bool, diff --git a/matrix_sdk_crypto/src/verification/qrcode.rs b/matrix_sdk_crypto/src/verification/qrcode.rs index baff6fab..a645a8e8 100644 --- a/matrix_sdk_crypto/src/verification/qrcode.rs +++ b/matrix_sdk_crypto/src/verification/qrcode.rs @@ -41,7 +41,7 @@ use tracing::trace; use super::{ event_enums::{CancelContent, DoneContent, OutgoingContent, OwnedStartContent, StartContent}, requests::RequestHandle, - Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult, + CancelInfo, Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, @@ -134,19 +134,11 @@ impl QrVerification { self.we_started } - /// Get the `CancelCode` that cancelled this verification request. - pub fn cancel_code(&self) -> Option { + /// Get info about the cancellation if the verification flow has been + /// cancelled. + pub fn cancel_info(&self) -> Option { if let InnerState::Cancelled(c) = &*self.state.lock().unwrap() { - Some(c.state.cancel_code.to_owned()) - } else { - None - } - } - - /// Has the verification flow been cancelled by us. - pub fn cancelled_by_us(&self) -> Option { - if let InnerState::Cancelled(c) = &*self.state.lock().unwrap() { - Some(c.state.cancelled_by_us) + Some(c.state.clone().into()) } else { None } diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 087728b6..e12abe10 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -42,7 +42,7 @@ use super::{ CancelContent, DoneContent, OutgoingContent, ReadyContent, RequestContent, StartContent, }, qrcode::{QrVerification, ScanError}, - Cancelled, FlowId, IdentitiesBeingVerified, + CancelInfo, Cancelled, FlowId, IdentitiesBeingVerified, }; use crate::{ olm::{PrivateCrossSigningIdentity, ReadOnlyAccount}, @@ -219,11 +219,13 @@ impl VerificationRequest { } } - /// Get the `CancelCode` that cancelled this verification request. - pub fn cancel_code(&self) -> Option { - match &*self.inner.lock().unwrap() { - InnerRequest::Cancelled(c) => Some(c.state.cancel_code.to_owned()), - _ => None, + /// Get info about the cancellation if the verification request has been + /// cancelled. + pub fn cancel_info(&self) -> Option { + if let InnerRequest::Cancelled(c) = &*self.inner.lock().unwrap() { + Some(c.state.clone().into()) + } else { + None } } diff --git a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs index 161181b6..53677e33 100644 --- a/matrix_sdk_crypto/src/verification/sas/inner_sas.rs +++ b/matrix_sdk_crypto/src/verification/sas/inner_sas.rs @@ -350,22 +350,6 @@ impl InnerSas { matches!(self, InnerSas::Confirmed(_) | InnerSas::WaitingForDone(_) | InnerSas::Done(_)) } - pub fn cancel_code(&self) -> Option { - if let InnerSas::Cancelled(c) = self { - Some(c.state.cancel_code.clone()) - } else { - None - } - } - - pub fn cancelled_by_us(&self) -> Option { - if let InnerSas::Cancelled(c) = self { - Some(c.state.cancelled_by_us) - } else { - None - } - } - pub fn timed_out(&self) -> bool { match self { InnerSas::Created(s) => s.timed_out(), diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index 56159d94..c382205f 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -35,7 +35,7 @@ use tracing::trace; use super::{ event_enums::{AnyVerificationContent, OutgoingContent, OwnedAcceptContent, StartContent}, requests::RequestHandle, - FlowId, IdentitiesBeingVerified, VerificationResult, + CancelInfo, FlowId, IdentitiesBeingVerified, VerificationResult, }; use crate::{ identities::{ReadOnlyDevice, ReadOnlyUserIdentities}, @@ -122,14 +122,14 @@ impl Sas { self.inner.lock().unwrap().has_been_accepted() } - /// Get the cancel code of this SAS verification if it has been cancelled - pub fn cancel_code(&self) -> Option { - self.inner.lock().unwrap().cancel_code() - } - - /// Has the verification flow been cancelled by us. - pub fn cancelled_by_us(&self) -> Option { - self.inner.lock().unwrap().cancelled_by_us() + /// Get info about the cancellation if the verification flow has been + /// cancelled. + pub fn cancel_info(&self) -> Option { + if let InnerSas::Cancelled(c) = &*self.inner.lock().unwrap() { + Some(c.state.as_ref().clone().into()) + } else { + None + } } /// Did we initiate the verification flow. From 0053d2a874f3e506efb1498b694bc9187444c5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:48:22 +0200 Subject: [PATCH 79/88] crypto: Don't send cancellations for passive verification requests --- matrix_sdk_crypto/src/verification/requests.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index e12abe10..41d1081d 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -465,7 +465,13 @@ impl VerificationRequest { if self.is_cancelled() || self.is_done() { None } else if self.timed_out() { - self.cancel_with_code(CancelCode::Timeout) + let request = self.cancel_with_code(CancelCode::Timeout); + + if self.is_passive() { + None + } else { + request + } } else { None } @@ -681,6 +687,12 @@ impl InnerRequest { } fn cancel(&mut self, cancelled_by_us: bool, cancel_code: &CancelCode) { + trace!( + cancelled_by_us = cancelled_by_us, + code = cancel_code.as_str(), + "Verification request going into the cancelled state" + ); + *self = InnerRequest::Cancelled(match self { InnerRequest::Created(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), InnerRequest::Requested(s) => s.clone().into_canceled(cancelled_by_us, cancel_code), From 8f03679935bc636b3cd3396d72075a9e532d95c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 09:59:08 +0200 Subject: [PATCH 80/88] matrix-sdk: Add more getters to the high level verification structs --- matrix_sdk/src/verification/mod.rs | 18 ++++++++++++++++++ matrix_sdk/src/verification/qrcode.rs | 19 ++++++++++++++++++- matrix_sdk/src/verification/requests.rs | 13 ++++++++++++- matrix_sdk/src/verification/sas.rs | 19 ++++++++++++++++++- 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/matrix_sdk/src/verification/mod.rs b/matrix_sdk/src/verification/mod.rs index a742badf..80a21f2b 100644 --- a/matrix_sdk/src/verification/mod.rs +++ b/matrix_sdk/src/verification/mod.rs @@ -33,6 +33,7 @@ mod qrcode; mod requests; mod sas; +pub use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo}; pub use qrcode::QrVerification; pub use requests::VerificationRequest; pub use sas::SasVerification; @@ -81,6 +82,15 @@ impl Verification { } } + /// Get info about the cancellation if the verification flow has been + /// cancelled. + pub fn cancel_info(&self) -> Option { + match self { + Verification::SasV1(s) => s.cancel_info(), + Verification::QrV1(q) => q.cancel_info(), + } + } + /// Get our own user id. pub fn own_user_id(&self) -> &ruma::UserId { match self { @@ -105,6 +115,14 @@ impl Verification { Verification::QrV1(v) => v.is_self_verification(), } } + + /// Did we initiate the verification flow. + pub fn we_started(&self) -> bool { + match self { + Verification::SasV1(s) => s.we_started(), + Verification::QrV1(q) => q.we_started(), + } + } } impl From for Verification { diff --git a/matrix_sdk/src/verification/qrcode.rs b/matrix_sdk/src/verification/qrcode.rs index f28d11b5..78b80e80 100644 --- a/matrix_sdk/src/verification/qrcode.rs +++ b/matrix_sdk/src/verification/qrcode.rs @@ -14,7 +14,7 @@ use matrix_sdk_base::crypto::{ matrix_qrcode::{qrcode::QrCode, EncodingError}, - QrVerification as BaseQrVerification, + CancelInfo, QrVerification as BaseQrVerification, }; use ruma::UserId; @@ -43,6 +43,23 @@ impl QrVerification { self.inner.is_done() } + /// Did we initiate the verification flow. + pub fn we_started(&self) -> bool { + self.inner.we_started() + } + + /// Get info about the cancellation if the verification flow has been + /// cancelled. + pub fn cancel_info(&self) -> Option { + self.inner.cancel_info() + } + + /// Get the user id of the other user participating in this verification + /// flow. + pub fn other_user_id(&self) -> &UserId { + self.inner.other_user_id() + } + /// Has the verification been cancelled. pub fn is_cancelled(&self) -> bool { self.inner.is_cancelled() diff --git a/matrix_sdk/src/verification/requests.rs b/matrix_sdk/src/verification/requests.rs index 5a94c7b9..b7b39485 100644 --- a/matrix_sdk/src/verification/requests.rs +++ b/matrix_sdk/src/verification/requests.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use matrix_sdk_base::crypto::VerificationRequest as BaseVerificationRequest; +use matrix_sdk_base::crypto::{CancelInfo, VerificationRequest as BaseVerificationRequest}; use ruma::events::key::verification::VerificationMethod; use super::{QrVerification, SasVerification}; @@ -36,6 +36,12 @@ impl VerificationRequest { self.inner.is_cancelled() } + /// Get info about the cancellation if the verification request has been + /// cancelled. + pub fn cancel_info(&self) -> Option { + self.inner.cancel_info() + } + /// Get our own user id. pub fn own_user_id(&self) -> &ruma::UserId { self.inner.own_user_id() @@ -51,6 +57,11 @@ impl VerificationRequest { self.inner.is_ready() } + /// Did we initiate the verification flow. + pub fn we_started(&self) -> bool { + self.inner.we_started() + } + /// Get the user id of the other user participating in this verification /// flow. pub fn other_user_id(&self) -> &ruma::UserId { diff --git a/matrix_sdk/src/verification/sas.rs b/matrix_sdk/src/verification/sas.rs index 76d1df92..3cb46641 100644 --- a/matrix_sdk/src/verification/sas.rs +++ b/matrix_sdk/src/verification/sas.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use matrix_sdk_base::crypto::{AcceptSettings, ReadOnlyDevice, Sas as BaseSas}; +use matrix_sdk_base::crypto::{AcceptSettings, CancelInfo, ReadOnlyDevice, Sas as BaseSas}; use ruma::UserId; use crate::{error::Result, Client}; @@ -121,6 +121,17 @@ impl SasVerification { self.inner.can_be_presented() } + /// Did we initiate the verification flow. + pub fn we_started(&self) -> bool { + self.inner.we_started() + } + + /// Get info about the cancellation if the verification flow has been + /// cancelled. + pub fn cancel_info(&self) -> Option { + self.inner.cancel_info() + } + /// Is the verification process canceled. pub fn is_cancelled(&self) -> bool { self.inner.is_cancelled() @@ -145,4 +156,10 @@ impl SasVerification { pub fn own_user_id(&self) -> &UserId { self.inner.user_id() } + + /// Get the user id of the other user participating in this verification + /// flow. + pub fn other_user_id(&self) -> &UserId { + self.inner.other_user_id() + } } From 5566886f208e9dfd06bc2b4c0236b8ce86b1dffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 10:26:39 +0200 Subject: [PATCH 81/88] crypto: Add public methods to request verifications with devices --- matrix_sdk_crypto/src/identities/device.rs | 49 ++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/matrix_sdk_crypto/src/identities/device.rs b/matrix_sdk_crypto/src/identities/device.rs index 9b432e77..5f782a60 100644 --- a/matrix_sdk_crypto/src/identities/device.rs +++ b/matrix_sdk_crypto/src/identities/device.rs @@ -28,7 +28,8 @@ use ruma::{ encryption::{DeviceKeys, SignedKey}, events::{ forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, - room::encrypted::EncryptedEventContent, AnyToDeviceEventContent, + key::verification::VerificationMethod, room::encrypted::EncryptedEventContent, + AnyToDeviceEventContent, }, DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId, }; @@ -43,7 +44,7 @@ use crate::{ olm::{InboundGroupSession, PrivateCrossSigningIdentity, Session, Utility}, store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult}, verification::VerificationMachine, - OutgoingVerificationRequest, Sas, ToDeviceRequest, + OutgoingVerificationRequest, Sas, ToDeviceRequest, VerificationRequest, }; #[cfg(test)] use crate::{OlmMachine, ReadOnlyAccount}; @@ -125,7 +126,13 @@ impl Deref for Device { impl Device { /// Start a interactive verification with this `Device` /// - /// Returns a `Sas` object and to-device request that needs to be sent out. + /// Returns a `Sas` object and a to-device request that needs to be sent + /// out. + /// + /// This method has been deprecated in the spec and the + /// [`request_verification()`] method should be used instead. + /// + /// [`request_verification()`]: #method.request_verification pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> { let (sas, request) = self.verification_machine.start_sas(self.inner.clone()).await?; @@ -136,6 +143,42 @@ impl Device { } } + /// Request an interacitve verification with this `Device` + /// + /// Returns a `VerificationRequest` object and a to-device request that + /// needs to be sent out. + pub async fn request_verification(&self) -> (VerificationRequest, OutgoingVerificationRequest) { + self.request_verification_helper(None).await + } + + /// Request an interacitve verification with this `Device` + /// + /// Returns a `VerificationRequest` object and a to-device request that + /// needs to be sent out. + /// + /// # Arguments + /// + /// * `methods` - The verification methods that we want to support. + pub async fn request_verification_with_methods( + &self, + methods: Vec, + ) -> (VerificationRequest, OutgoingVerificationRequest) { + self.request_verification_helper(Some(methods)).await + } + + async fn request_verification_helper( + &self, + methods: Option>, + ) -> (VerificationRequest, OutgoingVerificationRequest) { + self.verification_machine + .request_to_device_verification( + self.user_id(), + vec![self.device_id().to_owned()], + methods, + ) + .await + } + /// Get the Olm sessions that belong to this device. pub(crate) async fn get_sessions(&self) -> StoreResult>>>> { if let Some(k) = self.get_key(DeviceKeyAlgorithm::Curve25519) { From 24377a45ff6c329416749a8b5c9a016f767c2933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 10:34:34 +0200 Subject: [PATCH 82/88] matrix-sdk: Add methods to request verification for devices --- matrix_sdk/src/device.rs | 102 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/matrix_sdk/src/device.rs b/matrix_sdk/src/device.rs index 92c4fdaa..3d50a460 100644 --- a/matrix_sdk/src/device.rs +++ b/matrix_sdk/src/device.rs @@ -18,9 +18,13 @@ use matrix_sdk_base::crypto::{ store::CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice, UserDevices as BaseUserDevices, }; -use ruma::{DeviceId, DeviceIdBox}; +use ruma::{events::key::verification::VerificationMethod, DeviceId, DeviceIdBox}; -use crate::{error::Result, verification::SasVerification, Client}; +use crate::{ + error::Result, + verification::{SasVerification, VerificationRequest}, + Client, +}; #[derive(Clone, Debug)] /// A device represents a E2EE capable client of an user. @@ -43,7 +47,10 @@ impl Device { /// Returns a `Sas` object that represents the interactive verification /// flow. /// - /// # Example + /// This method has been deprecated in the spec and the + /// [`request_verification()`] method should be used instead. + /// + /// # Examples /// /// ```no_run /// # use std::convert::TryFrom; @@ -62,6 +69,8 @@ impl Device { /// let verification = device.start_verification().await.unwrap(); /// # }); /// ``` + /// + /// [`request_verification()`]: #method.request_verification pub async fn start_verification(&self) -> Result { let (sas, request) = self.inner.start_verification().await?; self.client.send_to_device(&request).await?; @@ -69,6 +78,93 @@ impl Device { Ok(SasVerification { inner: sas, client: self.client.clone() }) } + /// Request an interacitve verification with this `Device` + /// + /// Returns a `VerificationRequest` object and a to-device request that + /// needs to be sent out. + /// + /// The default methods that are supported are `m.sas.v1` and + /// `m.qr_code.show.v1`, if this isn't desireable the + /// [`request_verification_with_methods()`] method can be used to override + /// this. + /// + /// # Examples + /// + /// ```no_run + /// # use std::convert::TryFrom; + /// # use matrix_sdk::{Client, ruma::UserId}; + /// # use url::Url; + /// # use futures::executor::block_on; + /// # let alice = UserId::try_from("@alice:example.org").unwrap(); + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # let client = Client::new(homeserver).unwrap(); + /// # block_on(async { + /// let device = client.get_device(&alice, "DEVICEID".into()) + /// .await + /// .unwrap() + /// .unwrap(); + /// + /// let verification = device.request_verification().await.unwrap(); + /// # }); + /// ``` + /// + /// [`request_verification_with_methods()`]: + /// #method.request_verification_with_methods + pub async fn request_verification(&self) -> Result { + let (verification, request) = self.inner.request_verification().await; + self.client.send_verification_request(request).await?; + + Ok(VerificationRequest { inner: verification, client: self.client.clone() }) + } + + /// Request an interacitve verification with this `Device` + /// + /// Returns a `VerificationRequest` object and a to-device request that + /// needs to be sent out. + /// + /// # Arguments + /// + /// * `methods` - The verification methods that we want to support. + /// + /// # Examples + /// + /// ```no_run + /// # use std::convert::TryFrom; + /// # use matrix_sdk::{ + /// # Client, + /// # ruma::{ + /// # UserId, + /// # events::key::verification::VerificationMethod, + /// # } + /// # }; + /// # use url::Url; + /// # use futures::executor::block_on; + /// # let alice = UserId::try_from("@alice:example.org").unwrap(); + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # let client = Client::new(homeserver).unwrap(); + /// # block_on(async { + /// let device = client.get_device(&alice, "DEVICEID".into()) + /// .await + /// .unwrap() + /// .unwrap(); + /// + /// // We don't want to support showing a QR code, we only support SAS + /// // verification + /// let methods = vec![VerificationMethod::SasV1]; + /// + /// let verification = device.request_verification_with_methods(methods).await.unwrap(); + /// # }); + /// ``` + pub async fn request_verification_with_methods( + &self, + methods: Vec, + ) -> Result { + let (verification, request) = self.inner.request_verification_with_methods(methods).await; + self.client.send_verification_request(request).await?; + + Ok(VerificationRequest { inner: verification, client: self.client.clone() }) + } + /// Is the device considered to be verified, either by locally trusting it /// or using cross signing. pub fn verified(&self) -> bool { From 26310def0ad90021741bfd4192f097e4768978eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 10:49:22 +0200 Subject: [PATCH 83/88] crypto: Silence a couple of clippy warnings --- matrix_sdk_crypto/src/verification/requests.rs | 1 + matrix_sdk_crypto/src/verification/sas/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/matrix_sdk_crypto/src/verification/requests.rs b/matrix_sdk_crypto/src/verification/requests.rs index 41d1081d..f20d6d15 100644 --- a/matrix_sdk_crypto/src/verification/requests.rs +++ b/matrix_sdk_crypto/src/verification/requests.rs @@ -104,6 +104,7 @@ impl From>> for RequestHandle { } impl VerificationRequest { + #[allow(clippy::too_many_arguments)] pub(crate) fn new( cache: VerificationCache, account: ReadOnlyAccount, diff --git a/matrix_sdk_crypto/src/verification/sas/mod.rs b/matrix_sdk_crypto/src/verification/sas/mod.rs index c382205f..d4b06949 100644 --- a/matrix_sdk_crypto/src/verification/sas/mod.rs +++ b/matrix_sdk_crypto/src/verification/sas/mod.rs @@ -183,6 +183,7 @@ impl Sas { /// /// Returns the new `Sas` object and a `StartEventContent` that needs to be /// sent out through the server to the other device. + #[allow(clippy::too_many_arguments)] pub(crate) fn start( account: ReadOnlyAccount, private_identity: PrivateCrossSigningIdentity, From 3a8ff2f6b43f312b7582146ed712ff245ef9d5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 19 Jul 2021 11:19:14 +0200 Subject: [PATCH 84/88] matrix-sdk: Allow the key import error to be dead under WASM --- matrix_sdk/src/error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/matrix_sdk/src/error.rs b/matrix_sdk/src/error.rs index 2fe22129..6ebb9716 100644 --- a/matrix_sdk/src/error.rs +++ b/matrix_sdk/src/error.rs @@ -157,6 +157,8 @@ pub enum Error { #[cfg(feature = "encryption")] #[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[derive(Error, Debug)] +// This is allowed because key importing isn't enabled under wasm. +#[allow(dead_code)] pub enum RoomKeyImportError { /// An error de/serializing type for the `StateStore` #[error(transparent)] From 46f9c292ab3324da7d21bf60a7f648a418669734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Thu, 22 Jul 2021 16:02:32 +0200 Subject: [PATCH 85/88] crypto: Streamline how we update one-time key counts There is no reason to use different logic when updating one-time key counts coming from an upload response vs when coming from a sync response. Switch to an AtomicU64 for the count as well, the i64 is probably a remnant of the SQLite based crypto store. --- matrix_sdk_crypto/src/machine.rs | 10 ++++-- matrix_sdk_crypto/src/olm/account.rs | 54 +++++++++++++++------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/matrix_sdk_crypto/src/machine.rs b/matrix_sdk_crypto/src/machine.rs index f2f4f22e..1fe30a6b 100644 --- a/matrix_sdk_crypto/src/machine.rs +++ b/matrix_sdk_crypto/src/machine.rs @@ -736,8 +736,8 @@ impl OlmMachine { self.verification_machine.get_requests(user_id) } - async fn update_one_time_key_count(&self, key_count: &BTreeMap) { - self.account.update_uploaded_key_count(key_count).await; + fn update_one_time_key_count(&self, key_count: &BTreeMap) { + self.account.update_uploaded_key_count(key_count); } async fn handle_to_device_event(&self, event: &AnyToDeviceEvent) { @@ -796,7 +796,7 @@ impl OlmMachine { let mut changes = Changes { account: Some(self.account.inner.clone()), ..Default::default() }; - self.update_one_time_key_count(one_time_keys_counts).await; + self.update_one_time_key_count(one_time_keys_counts); for user_id in &changed_devices.changed { if let Err(e) = self.identity_manager.mark_user_as_changed(user_id).await { @@ -1410,6 +1410,10 @@ pub(crate) mod test { response.one_time_key_counts.insert(DeviceKeyAlgorithm::SignedCurve25519, uint!(50)); machine.receive_keys_upload_response(&response).await.unwrap(); assert!(!machine.should_upload_keys().await); + + response.one_time_key_counts.remove(&DeviceKeyAlgorithm::SignedCurve25519); + machine.receive_keys_upload_response(&response).await.unwrap(); + assert!(!machine.should_upload_keys().await); } #[tokio::test] diff --git a/matrix_sdk_crypto/src/olm/account.rs b/matrix_sdk_crypto/src/olm/account.rs index b7292e85..2931d789 100644 --- a/matrix_sdk_crypto/src/olm/account.rs +++ b/matrix_sdk_crypto/src/olm/account.rs @@ -14,11 +14,11 @@ use std::{ collections::BTreeMap, - convert::{TryFrom, TryInto}, + convert::TryInto, fmt, ops::Deref, sync::{ - atomic::{AtomicBool, AtomicI64, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, }; @@ -181,9 +181,22 @@ impl Account { } } - pub async fn update_uploaded_key_count(&self, key_count: &BTreeMap) { + pub fn update_uploaded_key_count(&self, key_count: &BTreeMap) { if let Some(count) = key_count.get(&DeviceKeyAlgorithm::SignedCurve25519) { let count: u64 = (*count).into(); + let old_count = self.inner.uploaded_key_count(); + + // Some servers might always return the key counts in the sync + // response, we don't want to the logs with noop changes if they do + // so. + if count != old_count { + debug!( + "Updated uploaded one-time key count {} -> {}.", + self.inner.uploaded_key_count(), + count + ); + } + self.inner.update_uploaded_key_count(count); } } @@ -197,16 +210,8 @@ impl Account { } self.inner.mark_as_shared(); - let one_time_key_count = - response.one_time_key_counts.get(&DeviceKeyAlgorithm::SignedCurve25519); - - let count: u64 = one_time_key_count.map_or(0, |c| (*c).into()); - debug!( - "Updated uploaded one-time key count {} -> {}, marking keys as published", - self.inner.uploaded_key_count(), - count - ); - self.inner.update_uploaded_key_count(count); + debug!("Marking one-time keys as published"); + self.update_uploaded_key_count(&response.one_time_key_counts); self.inner.mark_keys_as_published().await; self.store.save_account(self.inner.clone()).await?; @@ -432,7 +437,7 @@ pub struct ReadOnlyAccount { /// this is None, no action will be taken. After a sync request the client /// needs to set this for us, depending on the count we will suggest the /// client to upload new keys. - uploaded_signed_key_count: Arc, + uploaded_signed_key_count: Arc, } /// A typed representation of a base64 encoded string containing the account @@ -468,7 +473,7 @@ pub struct PickledAccount { /// Was the account shared. pub shared: bool, /// The number of uploaded one-time keys we have on the server. - pub uploaded_signed_key_count: i64, + pub uploaded_signed_key_count: u64, } #[cfg(not(tarpaulin_include))] @@ -499,7 +504,7 @@ impl ReadOnlyAccount { inner: Arc::new(Mutex::new(account)), identity_keys: Arc::new(identity_keys), shared: Arc::new(AtomicBool::new(false)), - uploaded_signed_key_count: Arc::new(AtomicI64::new(0)), + uploaded_signed_key_count: Arc::new(AtomicU64::new(0)), } } @@ -524,18 +529,17 @@ impl ReadOnlyAccount { /// /// * `new_count` - The new count that was reported by the server. pub(crate) fn update_uploaded_key_count(&self, new_count: u64) { - let key_count = i64::try_from(new_count).unwrap_or(i64::MAX); - self.uploaded_signed_key_count.store(key_count, Ordering::Relaxed); + self.uploaded_signed_key_count.store(new_count, Ordering::SeqCst); } /// Get the currently known uploaded key count. - pub fn uploaded_key_count(&self) -> i64 { - self.uploaded_signed_key_count.load(Ordering::Relaxed) + pub fn uploaded_key_count(&self) -> u64 { + self.uploaded_signed_key_count.load(Ordering::SeqCst) } /// Has the account been shared with the server. pub fn shared(&self) -> bool { - self.shared.load(Ordering::Relaxed) + self.shared.load(Ordering::SeqCst) } /// Mark the account as shared. @@ -543,7 +547,7 @@ impl ReadOnlyAccount { /// Messages shouldn't be encrypted with the session before it has been /// shared. pub(crate) fn mark_as_shared(&self) { - self.shared.store(true, Ordering::Relaxed); + self.shared.store(true, Ordering::SeqCst); } /// Get the one-time keys of the account. @@ -567,7 +571,7 @@ impl ReadOnlyAccount { /// /// Returns an empty error if no keys need to be uploaded. pub(crate) async fn generate_one_time_keys(&self) -> Result { - let count = self.uploaded_key_count() as u64; + let count = self.uploaded_key_count(); let max_keys = self.max_one_time_keys().await; let max_on_server = (max_keys as u64) / 2; @@ -588,7 +592,7 @@ impl ReadOnlyAccount { return true; } - let count = self.uploaded_key_count() as u64; + let count = self.uploaded_key_count(); // If we have a known key count, check that we have more than // max_one_time_Keys() / 2, otherwise tell the client to upload more. @@ -673,7 +677,7 @@ impl ReadOnlyAccount { inner: Arc::new(Mutex::new(account)), identity_keys: Arc::new(identity_keys), shared: Arc::new(AtomicBool::from(pickle.shared)), - uploaded_signed_key_count: Arc::new(AtomicI64::new(pickle.uploaded_signed_key_count)), + uploaded_signed_key_count: Arc::new(AtomicU64::new(pickle.uploaded_signed_key_count)), }) } From 4432be48e060227ffc6df5079f494aaf2c49a063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 26 Jul 2021 11:47:53 +0200 Subject: [PATCH 86/88] crypto: Fix a broken doc link --- matrix_sdk_crypto/src/identities/user.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/src/identities/user.rs b/matrix_sdk_crypto/src/identities/user.rs index 13ca15d6..178aae54 100644 --- a/matrix_sdk_crypto/src/identities/user.rs +++ b/matrix_sdk_crypto/src/identities/user.rs @@ -187,7 +187,7 @@ impl UserIdentity { /// user. /// /// After the content has been sent out a `VerificationRequest` can be - /// started with the [`request_verification`] method. + /// started with the [`request_verification()`] method. /// /// [`request_verification()`]: #method.request_verification pub async fn verification_request_content( From 778867e3ef8cb4f71ffef1294930ffe386c099e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 26 Jul 2021 15:28:17 +0200 Subject: [PATCH 87/88] crypto: Test that edits get properly decrypted --- matrix_sdk_crypto/Cargo.toml | 1 + matrix_sdk_crypto/src/olm/mod.rs | 75 +++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/matrix_sdk_crypto/Cargo.toml b/matrix_sdk_crypto/Cargo.toml index 7b42b582..80d168e4 100644 --- a/matrix_sdk_crypto/Cargo.toml +++ b/matrix_sdk_crypto/Cargo.toml @@ -48,6 +48,7 @@ byteorder = "1.4.3" [dev-dependencies] tokio = { version = "1.7.1", default-features = false, features = ["rt-multi-thread", "macros"] } proptest = "1.0.0" +matches = "0.1.8" serde_json = "1.0.64" tempfile = "3.2.0" http = "0.2.4" diff --git a/matrix_sdk_crypto/src/olm/mod.rs b/matrix_sdk_crypto/src/olm/mod.rs index 02767025..9abe2620 100644 --- a/matrix_sdk_crypto/src/olm/mod.rs +++ b/matrix_sdk_crypto/src/olm/mod.rs @@ -61,11 +61,19 @@ where pub(crate) mod test { use std::{collections::BTreeMap, convert::TryInto}; + use matches::assert_matches; use olm_rs::session::OlmMessage; use ruma::{ - encryption::SignedKey, events::forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, + encryption::SignedKey, + event_id, + events::{ + forwarded_room_key::ForwardedRoomKeyToDeviceEventContent, + room::message::{MessageEventContent, Relation, Replacement}, + AnyMessageEventContent, AnySyncMessageEvent, AnySyncRoomEvent, + }, room_id, user_id, DeviceId, UserId, }; + use serde_json::json; use crate::olm::{InboundGroupSession, ReadOnlyAccount, Session}; @@ -214,6 +222,71 @@ pub(crate) mod test { assert_eq!(plaintext, inbound.decrypt_helper(ciphertext).await.unwrap().0); } + #[tokio::test] + async fn edit_decryption() { + let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); + let room_id = room_id!("!test:localhost"); + let event_id = event_id!("$1234adfad:asdf"); + + let (outbound, _) = alice.create_group_session_pair_with_defaults(&room_id).await.unwrap(); + + assert_eq!(0, outbound.message_index().await); + assert!(!outbound.shared()); + outbound.mark_as_shared(); + assert!(outbound.shared()); + + let mut content = MessageEventContent::text_plain("Hello"); + content.relates_to = Some(Relation::Replacement(Replacement::new( + event_id.clone(), + MessageEventContent::text_plain("Hello edit").into(), + ))); + + let inbound = InboundGroupSession::new( + "test_key", + "test_key", + &room_id, + outbound.session_key().await, + None, + ) + .unwrap(); + + assert_eq!(0, inbound.first_known_index()); + + assert_eq!(outbound.session_id(), inbound.session_id()); + + let encrypted_content = + outbound.encrypt(AnyMessageEventContent::RoomMessage(content)).await; + + let event = json!({ + "sender": alice.user_id(), + "event_id": event_id, + "origin_server_ts": 0, + "room_id": room_id, + "type": "m.room.encrypted", + "content": encrypted_content, + }) + .to_string(); + + let event: AnySyncRoomEvent = serde_json::from_str(&event).expect("WHAAAT?!?!?"); + + let event = + if let AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomEncrypted(event)) = event { + event + } else { + panic!("Invalid event type") + }; + + let decrypted = inbound.decrypt(&event).await.unwrap().0; + + if let AnySyncRoomEvent::Message(AnySyncMessageEvent::RoomMessage(e)) = + decrypted.deserialize().unwrap() + { + assert_matches!(e.content.relates_to, Some(Relation::Replacement(_))); + } else { + panic!("Invalid event type") + } + } + #[tokio::test] async fn group_session_export() { let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id()); From 05eeba067ccb29acd576ce1e02705473c765d5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 26 Jul 2021 15:55:44 +0200 Subject: [PATCH 88/88] base: Add docs to the newly public hoist and deserialize method --- matrix_sdk_base/src/client.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/matrix_sdk_base/src/client.rs b/matrix_sdk_base/src/client.rs index 7c7497ea..399b5348 100644 --- a/matrix_sdk_base/src/client.rs +++ b/matrix_sdk_base/src/client.rs @@ -89,8 +89,8 @@ pub struct AdditionalUnsignedData { pub prev_content: Option>, } -/// Transform state event by hoisting `prev_content` field from `unsigned` to -/// the top level. +/// Transform an `AnySyncStateEvent` by hoisting `prev_content` field from +/// `unsigned` to the top level. /// /// Due to a [bug in synapse][synapse-bug], `prev_content` often ends up in /// `unsigned` contrary to the C2S spec. Some more discussion can be found @@ -129,6 +129,16 @@ fn hoist_member_event( Ok(e) } +/// Transform an `AnySyncRoomEvent` by hoisting `prev_content` field from +/// `unsigned` to the top level. +/// +/// Due to a [bug in synapse][synapse-bug], `prev_content` often ends up in +/// `unsigned` contrary to the C2S spec. Some more discussion can be found +/// [here][discussion]. Until this is fixed in synapse or handled in Ruma, we +/// use this to hoist up `prev_content` to the top level. +/// +/// [synapse-bug]: +/// [discussion]: pub fn hoist_room_event_prev_content( event: &Raw, ) -> StdResult {