crypto: Add device wrappers so that the verification can be started with a device.
This commit is contained in:
parent
9e609a0fdf
commit
fd8377bce2
8 changed files with 205 additions and 80 deletions
|
@ -38,7 +38,7 @@ use tracing::{error, info, instrument};
|
|||
use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
use matrix_sdk_base::{CryptoStoreError, Device, UserDevices};
|
||||
use matrix_sdk_base::CryptoStoreError;
|
||||
|
||||
use matrix_sdk_common::{
|
||||
api::r0::{
|
||||
|
@ -88,7 +88,11 @@ use crate::{
|
|||
};
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
use crate::{identifiers::DeviceId, sas::Sas};
|
||||
use crate::{
|
||||
device::{Device, UserDevices},
|
||||
identifiers::DeviceId,
|
||||
sas::Sas,
|
||||
};
|
||||
|
||||
const DEFAULT_SYNC_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
|
@ -1431,31 +1435,6 @@ impl Client {
|
|||
})
|
||||
}
|
||||
|
||||
/// Start a interactive verification with the given `Device`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `device` - The device which we would like to start an interactive
|
||||
/// verification with.
|
||||
///
|
||||
/// Returns a `Sas` object that represents the interactive verification flow.
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub async fn start_verification(&self, device: Device) -> Result<Sas> {
|
||||
let (sas, request) = self
|
||||
.base_client
|
||||
.start_verification(device)
|
||||
.await
|
||||
.ok_or(Error::AuthenticationRequired)?;
|
||||
|
||||
self.send_to_device(request).await?;
|
||||
|
||||
Ok(Sas {
|
||||
inner: sas,
|
||||
http_client: self.http_client.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a specific device of a user.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -1488,7 +1467,12 @@ impl Client {
|
|||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Option<Device> {
|
||||
self.base_client.get_device(user_id, device_id).await
|
||||
let device = self.base_client.get_device(user_id, device_id).await?;
|
||||
|
||||
Some(Device {
|
||||
inner: device,
|
||||
http_client: self.http_client.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a map holding all the devices of an user.
|
||||
|
@ -1502,7 +1486,7 @@ impl Client {
|
|||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use matrix_sdk::{Client, identifiers::UserId};
|
||||
/// # use url::Url;
|
||||
|
@ -1524,7 +1508,12 @@ impl Client {
|
|||
&self,
|
||||
user_id: &UserId,
|
||||
) -> StdResult<UserDevices, CryptoStoreError> {
|
||||
self.base_client.get_user_devices(user_id).await
|
||||
let devices = self.base_client.get_user_devices(user_id).await?;
|
||||
|
||||
Ok(UserDevices {
|
||||
inner: devices,
|
||||
http_client: self.http_client.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
80
matrix_sdk/src/device.rs
Normal file
80
matrix_sdk/src/device.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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::{DeviceWrap, UserDevicesWrap};
|
||||
use matrix_sdk_common::{
|
||||
api::r0::to_device::send_event_to_device::Request as ToDeviceRequest, identifiers::DeviceId,
|
||||
};
|
||||
|
||||
use crate::{error::Result, http_client::HttpClient, Sas};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// A device represents a E2EE capable client of an user.
|
||||
pub struct Device {
|
||||
pub(crate) inner: DeviceWrap,
|
||||
pub(crate) http_client: HttpClient,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Start a interactive verification with this `Device`
|
||||
///
|
||||
/// Returns a `Sas` object that represents the interactive verification flow.
|
||||
pub async fn start_verification(&self) -> Result<Sas> {
|
||||
let (sas, request) = self.inner.start_verification();
|
||||
let request = ToDeviceRequest {
|
||||
event_type: request.event_type,
|
||||
txn_id: &request.txn_id,
|
||||
messages: request.messages,
|
||||
};
|
||||
|
||||
self.http_client.send(request).await?;
|
||||
|
||||
Ok(Sas {
|
||||
inner: sas,
|
||||
http_client: self.http_client.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A read only view over all devices belonging to a user.
|
||||
#[derive(Debug)]
|
||||
pub struct UserDevices {
|
||||
pub(crate) inner: UserDevicesWrap,
|
||||
pub(crate) http_client: HttpClient,
|
||||
}
|
||||
|
||||
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,
|
||||
http_client: self.http_client.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterator over all the device ids of the user devices.
|
||||
pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
|
||||
self.inner.keys()
|
||||
}
|
||||
|
||||
/// Iterator over all the devices of the user devices.
|
||||
pub fn devices(&self) -> impl Iterator<Item = Device> + '_ {
|
||||
let client = self.http_client.clone();
|
||||
|
||||
self.inner.devices().map(move |d| Device {
|
||||
inner: d.clone(),
|
||||
http_client: client.clone(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -39,12 +39,12 @@
|
|||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use matrix_sdk_base::JsonStore;
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub use matrix_sdk_base::TrustState;
|
||||
pub use matrix_sdk_base::{
|
||||
CustomEvent, Error as BaseError, EventEmitter, Room, RoomState, Session, StateStore, SyncRoom,
|
||||
};
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub use matrix_sdk_base::{Device, TrustState};
|
||||
|
||||
#[cfg(feature = "messages")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(messages)))]
|
||||
|
@ -58,10 +58,15 @@ mod error;
|
|||
mod http_client;
|
||||
mod request_builder;
|
||||
|
||||
#[cfg(feature = "encryption")]
|
||||
mod device;
|
||||
#[cfg(feature = "encryption")]
|
||||
mod sas;
|
||||
|
||||
pub use client::{Client, ClientConfig, SyncSettings};
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub use device::Device;
|
||||
pub use error::{Error, Result};
|
||||
pub use http_client::HttpSend;
|
||||
pub use request_builder::{
|
||||
|
|
|
@ -53,7 +53,7 @@ use matrix_sdk_common::{
|
|||
};
|
||||
#[cfg(feature = "encryption")]
|
||||
use matrix_sdk_crypto::{
|
||||
CryptoStore, CryptoStoreError, Device, DeviceStore, OlmError, OlmMachine, Sas, UserDevices,
|
||||
CryptoStore, CryptoStoreError, DeviceWrap, OlmError, OlmMachine, Sas, UserDevicesWrap,
|
||||
};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
|
@ -1874,24 +1874,6 @@ impl BaseClient {
|
|||
.and_then(|o| o.get_verification(flow_id))
|
||||
}
|
||||
|
||||
/// Start a interactive verification with the given `Device`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `device` - The device which we would like to start an interactive
|
||||
/// verification with.
|
||||
///
|
||||
/// Returns a `Sas` object and a to-device request that needs to be sent out.
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub async fn start_verification(&self, device: Device) -> Option<(Sas, OwnedToDeviceRequest)> {
|
||||
self.olm
|
||||
.lock()
|
||||
.await
|
||||
.as_ref()
|
||||
.map(|o| o.start_verification(device))
|
||||
}
|
||||
|
||||
/// Get a specific device of a user.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -1922,7 +1904,7 @@ impl BaseClient {
|
|||
/// ```
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Option<Device> {
|
||||
pub async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Option<DeviceWrap> {
|
||||
let olm = self.olm.lock().await;
|
||||
olm.as_ref()?.get_device(user_id, device_id).await
|
||||
}
|
||||
|
@ -1936,9 +1918,14 @@ impl BaseClient {
|
|||
///
|
||||
/// * `user_id` - The unique id of the user that the devices belong to.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the client hasn't been logged in and the crypto layer thus
|
||||
/// hasn't been initialized.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use matrix_sdk_base::BaseClient;
|
||||
/// # use matrix_sdk_common::identifiers::UserId;
|
||||
|
@ -1958,12 +1945,14 @@ impl BaseClient {
|
|||
pub async fn get_user_devices(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
) -> StdResult<UserDevices, CryptoStoreError> {
|
||||
) -> StdResult<UserDevicesWrap, CryptoStoreError> {
|
||||
let olm = self.olm.lock().await;
|
||||
|
||||
if let Some(olm) = olm.as_ref() {
|
||||
Ok(olm.get_user_devices(user_id).await?)
|
||||
} else {
|
||||
Ok(DeviceStore::new().user_devices(user_id))
|
||||
// TODO remove this panic.
|
||||
panic!("The client hasn't been logged in")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ pub use state::{AllRooms, ClientState};
|
|||
|
||||
#[cfg(feature = "encryption")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||
pub use matrix_sdk_crypto::{CryptoStoreError, Device, Sas, TrustState, UserDevices};
|
||||
pub use matrix_sdk_crypto::{
|
||||
CryptoStoreError, Device, DeviceWrap, Sas, TrustState, UserDevicesWrap,
|
||||
};
|
||||
|
||||
#[cfg(feature = "messages")]
|
||||
#[cfg_attr(feature = "docs", doc(cfg(messages)))]
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
convert::TryFrom,
|
||||
ops::Deref,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
|
@ -23,7 +24,9 @@ use std::{
|
|||
|
||||
use atomic::Atomic;
|
||||
use matrix_sdk_common::{
|
||||
api::r0::keys::SignedKey,
|
||||
api::r0::{
|
||||
keys::SignedKey, to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
||||
},
|
||||
encryption::DeviceKeys,
|
||||
identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId},
|
||||
};
|
||||
|
@ -32,7 +35,9 @@ use serde_json::{json, Value};
|
|||
#[cfg(test)]
|
||||
use super::{Account, OlmMachine};
|
||||
|
||||
use crate::{error::SignatureError, verify_json};
|
||||
use crate::{
|
||||
error::SignatureError, verification::VerificationMachine, verify_json, Sas, UserDevices,
|
||||
};
|
||||
|
||||
/// A device represents a E2EE capable client of an user.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -47,6 +52,62 @@ pub struct Device {
|
|||
trust_state: Arc<Atomic<TrustState>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A device represents a E2EE capable client of an user.
|
||||
pub struct DeviceWrap {
|
||||
pub(crate) inner: Device,
|
||||
pub(crate) verification_machine: VerificationMachine,
|
||||
}
|
||||
|
||||
impl Deref for DeviceWrap {
|
||||
type Target = Device;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceWrap {
|
||||
/// Start a interactive verification with this `Device`
|
||||
///
|
||||
/// Returns a `Sas` object and to-device request that needs to be sent out.
|
||||
pub fn start_verification(&self) -> (Sas, OwnedToDeviceRequest) {
|
||||
self.verification_machine.start_sas(self.inner.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// A read only view over all devices belonging to a user.
|
||||
#[derive(Debug)]
|
||||
pub struct UserDevicesWrap {
|
||||
pub(crate) inner: UserDevices,
|
||||
pub(crate) verification_machine: VerificationMachine,
|
||||
}
|
||||
|
||||
impl UserDevicesWrap {
|
||||
/// Get the specific device with the given device id.
|
||||
pub fn get(&self, device_id: &DeviceId) -> Option<DeviceWrap> {
|
||||
self.inner.get(device_id).map(|d| DeviceWrap {
|
||||
inner: d,
|
||||
verification_machine: self.verification_machine.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterator over all the device ids of the user devices.
|
||||
pub fn keys(&self) -> impl Iterator<Item = &DeviceId> {
|
||||
self.inner.keys()
|
||||
}
|
||||
|
||||
/// Iterator over all the devices of the user devices.
|
||||
pub fn devices(&self) -> impl Iterator<Item = DeviceWrap> + '_ {
|
||||
let machine = self.verification_machine.clone();
|
||||
|
||||
self.inner.devices().map(move |d| DeviceWrap {
|
||||
inner: d.clone(),
|
||||
verification_machine: machine.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
/// The trust state of a device.
|
||||
pub enum TrustState {
|
||||
|
|
|
@ -37,7 +37,7 @@ mod store;
|
|||
mod user_identity;
|
||||
mod verification;
|
||||
|
||||
pub use device::{Device, TrustState};
|
||||
pub use device::{Device, DeviceWrap, TrustState, UserDevicesWrap};
|
||||
pub use error::{MegolmError, OlmError};
|
||||
pub use machine::{OlmMachine, OneTimeKeys};
|
||||
pub use memory_stores::{DeviceStore, GroupSessionStore, SessionStore, UserDevices};
|
||||
|
|
|
@ -53,7 +53,7 @@ use matrix_sdk_common::{
|
|||
#[cfg(feature = "sqlite_cryptostore")]
|
||||
use super::store::sqlite::SqliteStore;
|
||||
use super::{
|
||||
device::Device,
|
||||
device::{Device, DeviceWrap, UserDevicesWrap},
|
||||
error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult},
|
||||
olm::{
|
||||
Account, EncryptionSettings, GroupSessionKey, IdentityKeys, InboundGroupSession,
|
||||
|
@ -61,7 +61,7 @@ use super::{
|
|||
},
|
||||
store::{memorystore::MemoryStore, Result as StoreResult},
|
||||
verification::{Sas, VerificationMachine},
|
||||
CryptoStore, UserDevices,
|
||||
CryptoStore,
|
||||
};
|
||||
|
||||
/// A map from the algorithm and device id to a one-time key.
|
||||
|
@ -1136,18 +1136,6 @@ impl OlmMachine {
|
|||
self.verification_machine.get_sas(flow_id)
|
||||
}
|
||||
|
||||
/// Start a interactive verification with the given `Device`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `device` - The device which we would like to start an interactive
|
||||
/// verification with.
|
||||
///
|
||||
/// Returns a `Sas` object and to-device request that needs to be sent out.
|
||||
pub fn start_verification(&self, device: Device) -> (Sas, OwnedToDeviceRequest) {
|
||||
self.verification_machine.start_sas(device)
|
||||
}
|
||||
|
||||
/// Handle a sync response and update the internal state of the Olm machine.
|
||||
///
|
||||
/// This will decrypt to-device events but will not touch events in the room
|
||||
|
@ -1342,12 +1330,18 @@ impl OlmMachine {
|
|||
/// println!("{:?}", device);
|
||||
/// # });
|
||||
/// ```
|
||||
pub async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Option<Device> {
|
||||
self.store
|
||||
pub async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Option<DeviceWrap> {
|
||||
let device = self
|
||||
.store
|
||||
.get_device(user_id, device_id)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.flatten()?;
|
||||
|
||||
Some(DeviceWrap {
|
||||
inner: device,
|
||||
verification_machine: self.verification_machine.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a map holding all the devices of an user.
|
||||
|
@ -1373,8 +1367,13 @@ impl OlmMachine {
|
|||
/// }
|
||||
/// # });
|
||||
/// ```
|
||||
pub async fn get_user_devices(&self, user_id: &UserId) -> StoreResult<UserDevices> {
|
||||
self.store.get_user_devices(user_id).await
|
||||
pub async fn get_user_devices(&self, user_id: &UserId) -> StoreResult<UserDevicesWrap> {
|
||||
let devices = self.store.get_user_devices(user_id).await?;
|
||||
|
||||
Ok(UserDevicesWrap {
|
||||
inner: devices,
|
||||
verification_machine: self.verification_machine.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1999,7 +1998,7 @@ pub(crate) mod test {
|
|||
|
||||
assert!(!bob_device.is_trusted());
|
||||
|
||||
let (alice_sas, request) = alice.start_verification(bob_device.clone());
|
||||
let (alice_sas, request) = bob_device.start_verification();
|
||||
|
||||
let mut event = request_to_event(alice.user_id(), &request);
|
||||
bob.handle_verification_event(&mut event).await;
|
||||
|
|
Loading…
Reference in a new issue