matrix-rust-sdk/matrix_sdk/src/identities/devices.rs

316 lines
11 KiB
Rust

// 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<VerificationRequest> {
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<VerificationMethod>,
) -> Result<VerificationRequest> {
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<SasVerification> {
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<Device> {
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<Item = &DeviceIdBox> {
self.inner.keys()
}
/// Iterator over all the devices of the user devices.
pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
let client = self.client.clone();
self.inner.devices().map(move |d| Device { inner: d, client: client.clone() })
}
}