Skip to content

Commit

Permalink
Update the bindings to use the SAS signalling (#1300)
Browse files Browse the repository at this point in the history
* feat(bindings): Expose support for manually starting SAS verifications

Co-authored-by: Stefan Ceriu <stefanc@matrix.org>
  • Loading branch information
poljar and stefanceriu authored Jan 6, 2023
1 parent 8375c47 commit 6aff4fc
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 50 deletions.
5 changes: 5 additions & 0 deletions bindings/matrix-sdk-ffi/src/api.udl
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ interface AuthenticationService {
interface SessionVerificationEmoji {};

callback interface SessionVerificationControllerDelegate {
void did_accept_verification_request();
void did_start_sas_verification();
void did_receive_verification_data(sequence<SessionVerificationEmoji> data);
void did_fail();
void did_cancel();
Expand All @@ -318,6 +320,9 @@ interface SessionVerificationController {
[Throws=ClientError]
void request_verification();

[Throws=ClientError]
void start_sas_verification();

[Throws=ClientError]
void approve_verification();

Expand Down
3 changes: 2 additions & 1 deletion bindings/matrix-sdk-ffi/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,8 @@ impl Client {
.await?
.context("Failed retrieving user identity")?;

let session_verification_controller = SessionVerificationController::new(user_identity);
let session_verification_controller =
SessionVerificationController::new(self.client.encryption(), user_identity);

*self.session_verification_controller.write().await =
Some(session_verification_controller.clone());
Expand Down
151 changes: 102 additions & 49 deletions bindings/matrix-sdk-ffi/src/session_verification.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::sync::{Arc, RwLock};

use futures_util::StreamExt;
use matrix_sdk::{
encryption::{
identities::UserIdentity,
verification::{SasVerification, VerificationRequest},
verification::{SasState, SasVerification, VerificationRequest},
Encryption,
},
ruma::{
events::{key::verification::VerificationMethod, AnyToDeviceEvent},
Expand All @@ -30,16 +32,21 @@ impl SessionVerificationEmoji {
}

pub trait SessionVerificationControllerDelegate: Sync + Send {
fn did_accept_verification_request(&self);
fn did_start_sas_verification(&self);
fn did_receive_verification_data(&self, data: Vec<Arc<SessionVerificationEmoji>>);
fn did_fail(&self);
fn did_cancel(&self);
fn did_finish(&self);
}

pub type Delegate = Arc<RwLock<Option<Box<dyn SessionVerificationControllerDelegate>>>>;

#[derive(Clone)]
pub struct SessionVerificationController {
encryption: Encryption,
user_identity: UserIdentity,
delegate: Arc<RwLock<Option<Box<dyn SessionVerificationControllerDelegate>>>>,
delegate: Delegate,
verification_request: Arc<RwLock<Option<VerificationRequest>>>,
sas_verification: Arc<RwLock<Option<SasVerification>>>,
}
Expand All @@ -52,8 +59,9 @@ impl SessionVerificationController {
}

impl SessionVerificationController {
pub fn new(user_identity: UserIdentity) -> Self {
pub fn new(encryption: Encryption, user_identity: UserIdentity) -> Self {
SessionVerificationController {
encryption,
user_identity,
delegate: Arc::new(RwLock::new(None)),
verification_request: Arc::new(RwLock::new(None)),
Expand All @@ -76,6 +84,34 @@ impl SessionVerificationController {
})
}

pub fn start_sas_verification(&self) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
let verification_request = self.verification_request.read().unwrap().clone();

if let Some(verification) = verification_request {
match verification.start_sas().await {
Ok(Some(verification)) => {
*self.sas_verification.write().unwrap() = Some(verification.clone());

if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_start_sas_verification()
}

let delegate = self.delegate.clone();
RUNTIME.spawn(Self::listen_to_changes(delegate, verification));
}
_ => {
if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_fail()
}
}
}
}

Ok(())
})
}

pub fn approve_verification(&self) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
let sas_verification = self.sas_verification.read().unwrap().clone();
Expand Down Expand Up @@ -111,55 +147,43 @@ impl SessionVerificationController {

pub async fn process_to_device_message(&self, event: AnyToDeviceEvent) {
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) => {
// TODO: Use the changes stream for this as well once we expose
// VerificationRequest::changes() in the main crate.
AnyToDeviceEvent::KeyVerificationStart(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}

if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_cancel()
}
}
AnyToDeviceEvent::KeyVerificationKey(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}

if let Some(sas_verification) = &*self.sas_verification.read().unwrap() {
if let Some(emojis) = sas_verification.emoji() {
if let Some(delegate) = &*self.delegate.read().unwrap() {
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);
if let Some(verification) = self
.encryption
.get_verification(
self.user_identity.user_id(),
event.content.transaction_id.as_str(),
)
.await
{
if let Some(sas_verification) = verification.sas() {
*self.sas_verification.write().unwrap() = Some(sas_verification.clone());

if sas_verification.accept().await.is_ok() {
if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_start_sas_verification()
}

let delegate = self.delegate.clone();
RUNTIME.spawn(Self::listen_to_changes(delegate, sas_verification));
} else if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_fail()
}
} else if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_fail()
}
} else if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_fail()
}
}
AnyToDeviceEvent::KeyVerificationDone(event) => {
AnyToDeviceEvent::KeyVerificationReady(event) => {
if !self.is_transaction_id_valid(event.content.transaction_id.to_string()) {
return;
}

if let Some(delegate) = &*self.delegate.read().unwrap() {
delegate.did_finish()
delegate.did_accept_verification_request()
}
}
_ => (),
Expand All @@ -179,18 +203,47 @@ impl SessionVerificationController {
}
}

async fn start_sas_verification(&self) {
let verification_request = self.verification_request.read().unwrap().clone();
if let Some(verification) = verification_request {
match verification.start_sas().await {
Ok(verification) => {
*self.sas_verification.write().unwrap() = verification;
}
Err(_) => {
if let Some(delegate) = &*self.delegate.read().unwrap() {
async fn listen_to_changes(delegate: Delegate, sas: SasVerification) {
let mut stream = sas.changes();

while let Some(state) = stream.next().await {
match state {
SasState::KeysExchanged { emojis, decimals: _ } => {
// TODO: If emojis is None, decimals should be used.
if let Some(emojis) = emojis {
if let Some(delegate) = &*delegate.read().unwrap() {
let emojis = 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) = &*delegate.read().unwrap() {
delegate.did_fail()
}
}
SasState::Done { .. } => {
if let Some(delegate) = &*delegate.read().unwrap() {
delegate.did_finish()
}
break;
}
SasState::Cancelled(_cancel_info) => {
// TODO: The cancel_info is usable, we should tell the user why we were
// cancelled.
if let Some(delegate) = &*delegate.read().unwrap() {
delegate.did_cancel()
}
break;
}
SasState::Started { .. } | SasState::Accepted { .. } | SasState::Confirmed => (),
}
}
}
Expand Down

0 comments on commit 6aff4fc

Please sign in to comment.