matrix-sdk: Expose an API to start SAS verifications.

master
Damir Jelić 2020-08-12 11:39:47 +02:00
parent 42a4ad60e8
commit 7cb25361b2
4 changed files with 99 additions and 22 deletions

View File

@ -36,7 +36,12 @@ use tracing::{error, info, instrument};
use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore}; use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_common::api::r0::to_device::send_event_to_device::Request as ToDeviceRequest; use matrix_sdk_base::Device;
#[cfg(feature = "encryption")]
use matrix_sdk_common::api::r0::to_device::send_event_to_device::{
IncomingRequest as OwnedToDeviceRequest, Request as ToDeviceRequest,
};
use matrix_sdk_common::{ use matrix_sdk_common::{
identifiers::ServerName, identifiers::ServerName,
instant::{Duration, Instant}, instant::{Duration, Instant},
@ -52,7 +57,7 @@ use crate::{
events::{room::message::MessageEventContent, EventType}, events::{room::message::MessageEventContent, EventType},
http_client::{DefaultHttpClient, HttpClient, HttpSend}, http_client::{DefaultHttpClient, HttpClient, HttpSend},
identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId}, identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId},
Endpoint, EventEmitter, Result, Endpoint, Error, EventEmitter, Result,
}; };
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
@ -1082,13 +1087,26 @@ impl Client {
pub async fn send<Request>(&self, request: Request) -> Result<Request::IncomingResponse> pub async fn send<Request>(&self, request: Request) -> Result<Request::IncomingResponse>
where where
Request: Endpoint + Debug, Request: Endpoint + Debug,
crate::Error: From<FromHttpResponseError<Request::ResponseError>>, Error: From<FromHttpResponseError<Request::ResponseError>>,
{ {
self.http_client self.http_client
.send(request, self.base_client.session().clone()) .send(request, self.base_client.session().clone())
.await .await
} }
async fn send_to_device(
&self,
request: OwnedToDeviceRequest,
) -> Result<send_event_to_device::Response> {
let request = ToDeviceRequest {
event_type: request.event_type,
txn_id: &request.txn_id,
messages: request.messages,
};
self.send(request).await
}
/// Synchronize the client's state with the latest state on the server. /// Synchronize the client's state with the latest state on the server.
/// ///
/// If a `StateStore` is provided and this is the initial sync state will /// If a `StateStore` is provided and this is the initial sync state will
@ -1217,16 +1235,12 @@ impl Client {
} }
} }
for req in self.base_client.outgoing_to_device_requests().await { for request in self.base_client.outgoing_to_device_requests().await {
let request = ToDeviceRequest { let txn_id = request.txn_id.clone();
event_type: req.event_type,
txn_id: &req.txn_id,
messages: req.messages,
};
if self.send(request).await.is_ok() { if self.send_to_device(request).await.is_ok() {
self.base_client self.base_client
.mark_to_device_request_as_sent(&req.txn_id) .mark_to_device_request_as_sent(&txn_id)
.await; .await;
} }
} }
@ -1307,14 +1321,8 @@ impl Client {
.await .await
.expect("Keys don't need to be uploaded"); .expect("Keys don't need to be uploaded");
for req in requests.drain(..) { for request in requests.drain(..) {
let request = ToDeviceRequest { self.send_to_device(request).await?;
event_type: req.event_type,
txn_id: &req.txn_id,
messages: req.messages,
};
let _response: send_event_to_device::Response = self.send(request).await?;
} }
Ok(()) Ok(())
@ -1406,7 +1414,6 @@ impl Client {
/// Get a `Sas` verification object with the given flow id. /// Get a `Sas` verification object with the given flow id.
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))] #[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
#[instrument]
pub async fn get_verification(&self, flow_id: &str) -> Option<Sas> { pub async fn get_verification(&self, flow_id: &str) -> Option<Sas> {
self.base_client self.base_client
.get_verification(flow_id) .get_verification(flow_id)
@ -1418,6 +1425,33 @@ impl Client {
homeserver: self.homeserver.clone(), homeserver: self.homeserver.clone(),
}) })
} }
/// 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` that represents the interactive verification flow.
#[cfg(feature = "encryption")]
#[cfg_attr(docsrs, doc(cfg(feature = "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,
session: self.base_client.session().clone(),
http_client: self.http_client.clone(),
homeserver: self.homeserver.clone(),
})
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -51,7 +51,7 @@ use matrix_sdk_common::{
identifiers::{DeviceId, DeviceKeyAlgorithm}, identifiers::{DeviceId, DeviceKeyAlgorithm},
}; };
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_crypto::{CryptoStore, OlmError, OlmMachine, OneTimeKeys, Sas}; use matrix_sdk_crypto::{CryptoStore, Device, OlmError, OlmMachine, OneTimeKeys, Sas};
use zeroize::Zeroizing; use zeroize::Zeroizing;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -1849,6 +1849,7 @@ impl BaseClient {
/// Get a `Sas` verification object with the given flow id. /// Get a `Sas` verification object with the given flow id.
/// ///
/// # Arguments /// # Arguments
///
/// * `flow_id` - The unique id that identifies a interactive verification /// * `flow_id` - The unique id that identifies a interactive verification
/// flow. For in-room verifications this will be the event id of the /// flow. For in-room verifications this will be the event id of the
/// *m.key.verification.request* event that started the flow, for the /// *m.key.verification.request* event that started the flow, for the
@ -1863,6 +1864,24 @@ impl BaseClient {
.as_ref() .as_ref()
.and_then(|o| o.get_verification(flow_id)) .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 to-device request that needs to be sent out.
#[cfg(feature = "encryption")]
#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
pub async fn start_verification(&self, device: Device) -> Option<(Sas, OwnedToDeviceRequest)> {
self.olm
.lock()
.await
.as_ref()
.and_then(|o| Some(o.start_verification(device)))
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1057,6 +1057,19 @@ impl OlmMachine {
self.verification_machine.get_sas(flow_id) 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. /// 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 /// This will decrypt to-device events but will not touch events in the room

View File

@ -25,7 +25,7 @@ use matrix_sdk_common::{
}; };
use super::sas::{content_to_request, Sas}; use super::sas::{content_to_request, Sas};
use crate::{Account, CryptoStore, CryptoStoreError}; use crate::{Account, CryptoStore, CryptoStoreError, Device};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct VerificationMachine { pub struct VerificationMachine {
@ -45,6 +45,17 @@ impl VerificationMachine {
} }
} }
pub fn start_sas(&self, device: Device) -> (Sas, OwnedToDeviceRequest) {
let (sas, content) = Sas::start(self.account.clone(), device.clone(), self.store.clone());
let request = content_to_request(
device.user_id(),
device.device_id(),
AnyToDeviceEventContent::KeyVerificationStart(content),
);
(sas, request)
}
pub fn get_sas(&self, transaction_id: &str) -> Option<Sas> { pub fn get_sas(&self, transaction_id: &str) -> Option<Sas> {
#[allow(clippy::map_clone)] #[allow(clippy::map_clone)]
self.verifications.get(transaction_id).map(|s| s.clone()) self.verifications.get(transaction_id).map(|s| s.clone())