// 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 std::{ops::Deref, result::Result as StdResult}; use matrix_sdk_base::crypto::{ store::CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice, UserDevices as BaseUserDevices, }; use ruma::{events::key::verification::VerificationMethod, DeviceId, DeviceIdBox}; use super::ManualVerifyError; use crate::{ error::Result, verification::{SasVerification, VerificationRequest}, Client, }; /// A device represents a E2EE capable client or device of an user. /// /// A device is backed by [device keys] that are uploaded to the server. /// /// [device keys]: https://spec.matrix.org/unstable/client-server-api/#device-keys #[derive(Clone, Debug)] pub struct Device { pub(crate) inner: BaseDevice, pub(crate) client: Client, } impl Deref for Device { type Target = ReadOnlyDevice; fn deref(&self) -> &Self::Target { &self.inner } } impl Device { /// Request an interactive verification with this `Device`. /// /// Returns a [`VerificationRequest`] object that can be used to control the /// verification flow. /// /// The default methods that are supported are `m.sas.v1` and /// `m.qr_code.show.v1`, if this isn't desirable the /// [`request_verification_with_methods()`] method can be used to override /// this. `m.qr_code.show.v1` is only avaliable if the `qrcode` feature is /// enabled, which it is by default. /// /// # 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?; /// /// if let Some(device) = device { /// let verification = device.request_verification().await?; /// } /// # anyhow::Result::<()>::Ok(()) }); /// ``` /// /// [`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 interactive verification with this `Device`. /// /// Returns a [`VerificationRequest`] object that can be used to control the /// verification flow. /// /// # Arguments /// /// * `methods` - The verification methods that we want to support. Must be /// non-empty. /// /// # Panics /// /// This method will panic if `methods` is empty. /// /// # 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?; /// /// // We don't want to support showing a QR code, we only support SAS /// // verification /// let methods = vec![VerificationMethod::SasV1]; /// /// if let Some(device) = device { /// let verification = device.request_verification_with_methods(methods).await?; /// } /// # anyhow::Result::<()>::Ok(()) }); /// ``` pub async fn request_verification_with_methods( &self, methods: Vec, ) -> Result { if methods.is_empty() { panic!("The list of verification methods can't be non-empty"); } 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() }) } /// Start an interactive verification with this [`Device`] /// /// Returns a [`SasVerification`] object that represents the interactive /// verification flow. /// /// This method has been deprecated in the spec and the /// [`request_verification()`] method should be used instead. /// /// # 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?; /// /// if let Some(device) = device { /// let verification = device.start_verification().await?; /// } /// # anyhow::Result::<()>::Ok(()) }); /// ``` /// /// [`request_verification()`]: #method.request_verification #[deprecated( since = "0.4.0", note = "directly starting a verification is deprecated in the spec. \ Users should instead use request_verification()" )] pub async fn start_verification(&self) -> Result { let (sas, request) = self.inner.start_verification().await?; self.client.send_to_device(&request).await?; Ok(SasVerification { inner: sas, client: self.client.clone() }) } /// Manually verify this device. /// /// This method will attempt to sign the device using our private cross /// signing key. /// /// This method will always fail if the device belongs to someone else, we /// can only sign our own devices. /// /// It can also fail if we don't have the private part of our self-signing /// key. /// /// The state of our private cross signing keys can be inspected using the /// [`Client::cross_signing_status()`] method. /// /// [`Client::cross_signing_status()`]: crate::Client::cross_signing_status /// /// # 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?; /// /// if let Some(device) = device { /// device.verify().await?; /// } /// # anyhow::Result::<()>::Ok(()) }); /// ``` pub async fn verify(&self) -> std::result::Result<(), ManualVerifyError> { let request = self.inner.verify().await?; self.client.send(request, None).await?; Ok(()) } /// Is the device considered to be verified. /// /// A device is considered to be verified, either if it's locally trusted, /// or if it's signed by the appropriate cross signing key. /// /// If the device belongs to our own userk, the device needs to be signed by /// our self-signing key and our own user identity needs to be verified. /// /// If the device belongs to some other user, the device needs to be signed /// by the users signing key and the user identity of the user needs to be /// verified. //// # 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 user = client.get_user_identity(&alice).await?; /// /// if let Some(user) = user { /// if user.verified() { /// println!("User {} is verified", user.user_id().as_str()); /// } else { /// println!("User {} is not verified", user.user_id().as_str()); /// } /// } /// # anyhow::Result::<()>::Ok(()) }); /// ``` pub fn verified(&self) -> bool { self.inner.verified() } /// Set the local trust state of the device to the given state. /// /// This won't affect any cross signing trust state, this only sets a flag /// marking to have the given trust state. /// /// # Arguments /// /// * `trust_state` - The new trust state that should be set for the device. pub async fn set_local_trust( &self, trust_state: LocalTrust, ) -> StdResult<(), CryptoStoreError> { self.inner.set_local_trust(trust_state).await } } /// The collection of all the [`Device`]s a user has. #[derive(Debug)] pub struct UserDevices { pub(crate) inner: BaseUserDevices, pub(crate) client: Client, } impl UserDevices { /// Get the specific device with the given device id. pub fn get(&self, device_id: &DeviceId) -> Option { self.inner.get(device_id).map(|d| Device { inner: d, client: self.client.clone() }) } /// Iterator over all the device ids of the user devices. pub fn keys(&self) -> impl Iterator { self.inner.keys() } /// Iterator over all the devices of the user devices. pub fn devices(&self) -> impl Iterator + '_ { let client = self.client.clone(); self.inner.devices().map(move |d| Device { inner: d, client: client.clone() }) } }