feat(bindings): Session verification through FFI

This commit is contained in:
Stefan Ceriu
2022-06-29 13:59:52 +02:00
committed by GitHub
parent e2ca56114e
commit 8a2d13feea
6 changed files with 276 additions and 6 deletions
+1 -1
View File
@@ -64,7 +64,7 @@ if [ "$IS_CI" = false ] ; then
echo "Preparing matrix-rust-components-swift"
# Debug -> Copy generated files over to ../../../matrix-rust-components-swift
echo "$(echo "import MatrixSDKFFIWrapper\n"; cat "${SWIFT_DIR}/sdk.swift")" > "${SWIFT_DIR}/sdk.swift"
echo "$(printf "import MatrixSDKFFIWrapper\n\n"; cat "${SWIFT_DIR}/sdk.swift")" > "${SWIFT_DIR}/sdk.swift"
rsync -a --delete "${GENERATED_DIR}/MatrixSDKFFI.xcframework" "${SRC_ROOT}/../matrix-rust-components-swift/"
rsync -a --delete "${GENERATED_DIR}/swift/" "${SRC_ROOT}/../matrix-rust-components-swift/Sources/MatrixRustSDK"
+1 -1
View File
@@ -10,7 +10,7 @@ rust-version = "1.56"
repository = "https://github.com/matrix-org/matrix-rust-sdk"
[lib]
crate-type = ["cdylib", "staticlib"]
crate-type = ["staticlib"]
[build-dependencies]
+33
View File
@@ -65,6 +65,9 @@ interface Client {
[Throws=ClientError]
sequence<u8> get_media_content(MediaSource source);
[Throws=ClientError]
SessionVerificationController get_session_verification_controller();
};
callback interface RoomDelegate {
@@ -148,3 +151,33 @@ interface EmoteMessage {
interface MediaSource {
string url();
};
interface SessionVerificationEmoji {
string symbol();
string description();
};
callback interface SessionVerificationControllerDelegate {
void did_receive_verification_data(sequence<SessionVerificationEmoji> data);
void did_fail();
void did_cancel();
void did_finish();
};
interface SessionVerificationController {
void set_delegate(SessionVerificationControllerDelegate? delegate);
boolean is_verified();
[Throws=ClientError]
void request_verification();
[Throws=ClientError]
void approve_verification();
[Throws=ClientError]
void decline_verification();
[Throws=ClientError]
void cancel_verification();
};
+46 -3
View File
@@ -15,7 +15,10 @@ use matrix_sdk::{
};
use parking_lot::RwLock;
use super::{room::Room, ClientState, RestoreToken, RUNTIME};
use super::{
room::Room, session_verification::SessionVerificationController, ClientState, RestoreToken,
RUNTIME,
};
impl std::ops::Deref for Client {
type Target = MatrixClient;
@@ -33,6 +36,8 @@ pub struct Client {
client: MatrixClient,
state: Arc<RwLock<ClientState>>,
delegate: Arc<RwLock<Option<Box<dyn ClientDelegate>>>>,
session_verification_controller:
Arc<matrix_sdk::locks::RwLock<Option<SessionVerificationController>>>,
}
impl Client {
@@ -41,6 +46,7 @@ impl Client {
client,
state: Arc::new(RwLock::new(state)),
delegate: Arc::new(RwLock::new(None)),
session_verification_controller: Arc::new(matrix_sdk::locks::RwLock::new(None)),
}
}
@@ -69,6 +75,7 @@ impl Client {
let client = self.client.clone();
let state = self.state.clone();
let delegate = self.delegate.clone();
let session_verification_controller = self.session_verification_controller.clone();
RUNTIME.spawn(async move {
let mut filter = FilterDefinition::default();
let mut room_filter = RoomFilter::default();
@@ -84,7 +91,7 @@ impl Client {
let sync_settings = SyncSettings::new().filter(Filter::FilterId(&filter_id));
client
.sync_with_callback(sync_settings, |_| async {
.sync_with_callback(sync_settings, |sync_response| async {
if !state.read().has_first_synced {
state.write().has_first_synced = true
}
@@ -96,9 +103,18 @@ impl Client {
state.write().is_syncing = true;
}
if let Some(ref delegate) = *delegate.read() {
if let Some(delegate) = &*delegate.read() {
delegate.did_receive_sync_update()
}
if let Some(session_verification_controller) =
&*session_verification_controller.read().await
{
session_verification_controller
.process_to_device_messages(sync_response.to_device)
.await;
}
LoopCtrl::Continue
})
.await;
@@ -172,6 +188,33 @@ impl Client {
.await?)
})
}
pub fn get_session_verification_controller(
&self,
) -> anyhow::Result<Arc<SessionVerificationController>> {
RUNTIME.block_on(async move {
if let Some(session_verification_controller) =
&*self.session_verification_controller.read().await
{
return Ok(Arc::new(session_verification_controller.clone()));
}
let user_id = self.client.user_id().expect("Failed retrieving current user_id");
let user_identity = self
.client
.encryption()
.get_user_identity(user_id)
.await?
.expect("Failed retrieving user identity");
let session_verification_controller = SessionVerificationController::new(user_identity);
*self.session_verification_controller.write().await =
Some(session_verification_controller.clone());
Ok(Arc::new(session_verification_controller))
})
}
}
pub fn gen_transaction_id() -> String {
+2 -1
View File
@@ -7,6 +7,7 @@ pub mod client;
pub mod client_builder;
pub mod messages;
pub mod room;
pub mod session_verification;
mod uniffi_api;
use client::Client;
@@ -22,7 +23,7 @@ pub static RUNTIME: Lazy<Runtime> =
pub use matrix_sdk::ruma::{api::client::account::register, UserId};
pub use self::{backward_stream::*, client::*, messages::*, room::*};
pub use self::{backward_stream::*, client::*, messages::*, room::*, session_verification::*};
#[derive(Default, Debug)]
pub struct ClientState {
@@ -0,0 +1,193 @@
use std::sync::Arc;
use matrix_sdk::{
encryption::{
identities::UserIdentity,
verification::{SasVerification, VerificationRequest},
},
ruma::{
api::client::sync::sync_events::v3::ToDevice,
events::{key::verification::VerificationMethod, AnyToDeviceEvent},
},
};
use parking_lot::RwLock;
use super::RUNTIME;
pub struct SessionVerificationEmoji {
symbol: String,
description: String,
}
impl SessionVerificationEmoji {
pub fn symbol(&self) -> String {
self.symbol.clone()
}
pub fn description(&self) -> String {
self.description.clone()
}
}
pub trait SessionVerificationControllerDelegate: Sync + Send {
fn did_receive_verification_data(&self, data: Vec<Arc<SessionVerificationEmoji>>);
fn did_fail(&self);
fn did_cancel(&self);
fn did_finish(&self);
}
#[derive(Clone)]
pub struct SessionVerificationController {
user_identity: UserIdentity,
delegate: Arc<RwLock<Option<Box<dyn SessionVerificationControllerDelegate>>>>,
verification_request: Arc<RwLock<Option<VerificationRequest>>>,
sas_verification: Arc<RwLock<Option<SasVerification>>>,
}
impl SessionVerificationController {
pub fn new(user_identity: UserIdentity) -> Self {
SessionVerificationController {
user_identity,
delegate: Arc::new(RwLock::new(None)),
verification_request: Arc::new(RwLock::new(None)),
sas_verification: Arc::new(RwLock::new(None)),
}
}
pub fn set_delegate(&self, delegate: Option<Box<dyn SessionVerificationControllerDelegate>>) {
*self.delegate.write() = delegate;
}
pub fn is_verified(&self) -> bool {
self.user_identity.verified()
}
pub fn request_verification(&self) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
let methods = vec![VerificationMethod::SasV1];
let verification_request =
self.user_identity.request_verification_with_methods(methods).await?;
*self.verification_request.write() = Some(verification_request);
Ok(())
})
}
pub fn approve_verification(&self) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
let sas_verification = self.sas_verification.read().clone();
if let Some(sas_verification) = sas_verification {
sas_verification.confirm().await?;
}
Ok(())
})
}
pub fn decline_verification(&self) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
let sas_verification = self.sas_verification.read().clone();
if let Some(sas_verification) = sas_verification {
sas_verification.mismatch().await?;
}
Ok(())
})
}
pub fn cancel_verification(&self) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
let verification_request = self.verification_request.read().clone();
if let Some(verification) = verification_request {
verification.cancel().await?;
}
Ok(())
})
}
pub async fn process_to_device_messages(&self, to_device: ToDevice) {
let sas_verification = self.sas_verification.clone();
for event in to_device.events.into_iter().filter_map(|e| e.deserialize().ok()) {
match event {
AnyToDeviceEvent::KeyVerificationReady(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}
self.start_sas_verification().await;
}
AnyToDeviceEvent::KeyVerificationCancel(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}
if let Some(delegate) = &*self.delegate.read() {
delegate.did_cancel()
}
}
AnyToDeviceEvent::KeyVerificationKey(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}
if let Some(sas_verification) = &*sas_verification.read() {
if let Some(emojis) = sas_verification.emoji() {
if let Some(delegate) = &*self.delegate.read() {
let emojis = emojis
.iter()
.map(|e| {
Arc::new(SessionVerificationEmoji {
symbol: e.symbol.to_owned(),
description: e.description.to_owned(),
})
})
.collect::<Vec<_>>();
delegate.did_receive_verification_data(emojis);
}
} else if let Some(delegate) = &*self.delegate.read() {
delegate.did_fail()
}
} else if let Some(delegate) = &*self.delegate.read() {
delegate.did_fail()
}
}
AnyToDeviceEvent::KeyVerificationDone(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}
if let Some(delegate) = &*self.delegate.read() {
delegate.did_finish()
}
}
_ => (),
}
}
}
fn is_transaction_id_valid(&self, transaction_id: String) -> bool {
if let Some(verification) = &*self.verification_request.read() {
return verification.flow_id() == transaction_id;
}
false
}
async fn start_sas_verification(&self) {
let verification_request = self.verification_request.read().clone();
if let Some(verification) = verification_request {
match verification.start_sas().await {
Ok(verification) => {
*self.sas_verification.write() = verification;
}
Err(_) => {
if let Some(delegate) = &*self.delegate.read() {
delegate.did_fail()
}
}
}
}
}
}