Skip to content

Commit

Permalink
Verification request and QR signalling
Browse files Browse the repository at this point in the history
  • Loading branch information
Anderas committed Dec 14, 2022
1 parent 1ef645d commit 68ce25a
Show file tree
Hide file tree
Showing 8 changed files with 471 additions and 113 deletions.
5 changes: 3 additions & 2 deletions bindings/matrix-sdk-crypto-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ use ruma::{
use serde::{Deserialize, Serialize};
pub use users::UserIdentity;
pub use verification::{
CancelInfo, ConfirmVerificationResult, QrCode, RequestVerificationResult, Sas, SasListener,
SasState, ScanResult, StartSasResult, Verification, VerificationRequest,
CancelInfo, ConfirmVerificationResult, QrCode, QrCodeListener, QrCodeState,
RequestVerificationResult, Sas, SasListener, SasState, ScanResult, StartSasResult,
Verification, VerificationRequest, VerificationRequestListener, VerificationRequestState,
};

/// Struct collecting data that is important to migrate to the rust-sdk
Expand Down
34 changes: 33 additions & 1 deletion bindings/matrix-sdk-crypto-ffi/src/olm.udl
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ interface Sas {
sequence<i32>? get_emoji_indices();
sequence<i32>? get_decimals();

void set_changes_listener(SasListener callback);
void set_changes_listener(SasListener listener);
SasState state();
};

Expand Down Expand Up @@ -201,6 +201,23 @@ interface QrCode {
ConfirmVerificationResult? confirm();
OutgoingVerificationRequest? cancel([ByRef] string cancel_code);
string? generate_qr_code();

void set_changes_listener(QrCodeListener listener);
QrCodeState state();
};

[Enum]
interface QrCodeState {
Started();
Scanned();
Confirmed();
Reciprocated();
Done();
Cancelled(CancelInfo cancel_info);
};

callback interface QrCodeListener {
void on_change(QrCodeState state);
};

interface VerificationRequest {
Expand Down Expand Up @@ -228,6 +245,21 @@ interface VerificationRequest {
ScanResult? scan_qr_code([ByRef] string data);

OutgoingVerificationRequest? cancel();

void set_changes_listener(VerificationRequestListener listener);
VerificationRequestState state();
};

[Enum]
interface VerificationRequestState {
Requested();
Ready();
Done();
Cancelled(CancelInfo cancel_info);
};

callback interface VerificationRequestListener {
void on_change(VerificationRequestState state);
};

dictionary RequestVerificationResult {
Expand Down
190 changes: 170 additions & 20 deletions bindings/matrix-sdk-crypto-ffi/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ use base64::{decode_config, encode_config, STANDARD_NO_PAD};
use futures_util::{Stream, StreamExt};
use matrix_sdk_crypto::{
matrix_sdk_qrcode::QrVerificationData, CancelInfo as RustCancelInfo, QrVerification as InnerQr,
Sas as InnerSas, SasState as RustSasState, Verification as InnerVerification,
VerificationRequest as InnerVerificationRequest,
QrVerificationState, Sas as InnerSas, SasState as RustSasState,
Verification as InnerVerification, VerificationRequest as InnerVerificationRequest,
VerificationRequestState as RustVerificationRequestState,
};
use ruma::events::key::verification::VerificationMethod;
use tokio::runtime::Handle;

use crate::{CryptoStoreError, OutgoingVerificationRequest, SignatureUploadRequest};

/// Callback that will be passed over the FFI to report changes to a SAS
/// Listener that will be passed over the FFI to report changes to a SAS
/// verification.
pub trait SasListener: Send {
/// The callback that should be called on the Rust side
Expand All @@ -23,15 +24,6 @@ pub trait SasListener: Send {
fn on_change(&self, state: SasState);
}

impl<T: Fn(SasState)> SasListener for T
where
T: Send,
{
fn on_change(&self, state: SasState) {
self(state)
}
}

/// An Enum describing the state the SAS verification is in.
pub enum SasState {
/// The verification has been started, the protocols that should be used
Expand Down Expand Up @@ -98,7 +90,7 @@ impl Verification {
/// returns `None` if the verification is not a `QrCode` verification.
pub fn as_qr(&self) -> Option<Arc<QrCode>> {
if let InnerVerification::QrV1(qr) = &self.inner {
Some(QrCode { inner: qr.to_owned() }.into())
Some(QrCode { inner: qr.to_owned(), runtime: self.runtime.to_owned() }.into())
} else {
None
}
Expand Down Expand Up @@ -239,20 +231,20 @@ impl Sas {
/// │ Done │
/// └───────┘
/// ```
pub fn set_changes_listener(&self, callback: Box<dyn SasListener>) {
pub fn set_changes_listener(&self, listener: Box<dyn SasListener>) {
let stream = self.inner.changes();

self.runtime.spawn(Self::changes_callback(stream, callback));
self.runtime.spawn(Self::changes_listener(stream, listener));
}

/// Get the current state of the SAS verification process.
pub fn state(&self) -> SasState {
self.inner.state().into()
}

async fn changes_callback(
async fn changes_listener(
mut stream: impl Stream<Item = RustSasState> + std::marker::Unpin,
callback: Box<dyn SasListener>,
listener: Box<dyn SasListener>,
) {
while let Some(state) = stream.next().await {
// If we receive a done or a cancelled state we're at the end of our road, we
Expand All @@ -261,7 +253,7 @@ impl Sas {
let should_break =
matches!(state, RustSasState::Done { .. } | RustSasState::Cancelled { .. });

callback.on_change(state.into());
listener.on_change(state.into());

if should_break {
break;
Expand All @@ -270,10 +262,55 @@ impl Sas {
}
}

/// Listener that will be passed over the FFI to report changes to a QrCode
/// verification.
pub trait QrCodeListener: Send {
/// The callback that should be called on the Rust side
///
/// # Arguments
///
/// * `state` - The current state of the QrCode verification.
fn on_change(&self, state: QrCodeState);
}

/// An Enum describing the state the QrCode verification is in.
pub enum QrCodeState {
/// The QR verification has been started.
Started,
/// The QR verification has been scanned by the other side.
Scanned,
/// The scanning of the QR code has been confirmed by us.
Confirmed,
/// We have successfully scanned the QR code and are able to send a
/// reciprocation event.
Reciprocated,
/// The verification process has been successfully concluded.
Done,
/// The verification process has been cancelled.
Cancelled {
/// Information about the reason of the cancellation.
cancel_info: CancelInfo,
},
}

impl From<QrVerificationState> for QrCodeState {
fn from(value: QrVerificationState) -> Self {
match value {
QrVerificationState::Started => Self::Started,
QrVerificationState::Scanned => Self::Scanned,
QrVerificationState::Confirmed => Self::Confirmed,
QrVerificationState::Reciprocated => Self::Reciprocated,
QrVerificationState::Done { .. } => Self::Done,
QrVerificationState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
}
}
}

/// The `m.qr_code.scan.v1`, `m.qr_code.show.v1`, and `m.reciprocate.v1`
/// verification flow.
pub struct QrCode {
pub(crate) inner: InnerQr,
pub(crate) runtime: Handle,
}

impl QrCode {
Expand Down Expand Up @@ -366,6 +403,41 @@ impl QrCode {
pub fn generate_qr_code(&self) -> Option<String> {
self.inner.to_bytes().map(|data| encode_config(data, STANDARD_NO_PAD)).ok()
}

/// Set a listener for changes in the QrCode verification process.
///
/// The given callback will be called whenever the state changes.
pub fn set_changes_listener(&self, listener: Box<dyn QrCodeListener>) {
let stream = self.inner.changes();

self.runtime.spawn(Self::changes_listener(stream, listener));
}

/// Get the current state of the QrCode verification process.
pub fn state(&self) -> QrCodeState {
self.inner.state().into()
}

async fn changes_listener(
mut stream: impl Stream<Item = QrVerificationState> + std::marker::Unpin,
listener: Box<dyn QrCodeListener>,
) {
while let Some(state) = stream.next().await {
// If we receive a done or a cancelled state we're at the end of our road, we
// break out of the loop to deallocate the stream and finish the
// task.
let should_break = matches!(
state,
QrVerificationState::Done { .. } | QrVerificationState::Cancelled { .. }
);

listener.on_change(state.into());

if should_break {
break;
}
}
}
}

/// Information on why a verification flow has been cancelled and by whom.
Expand Down Expand Up @@ -425,6 +497,45 @@ pub struct ConfirmVerificationResult {
pub signature_request: Option<SignatureUploadRequest>,
}

/// Listener that will be passed over the FFI to report changes to a
/// verification request.
pub trait VerificationRequestListener: Send {
/// The callback that should be called on the Rust side
///
/// # Arguments
///
/// * `state` - The current state of the verification request.
fn on_change(&self, state: VerificationRequestState);
}

/// An Enum describing the state the QrCode verification is in.
pub enum VerificationRequestState {
/// The verification request was sent
Requested,
/// The verification request is ready to start a verification flow.
Ready,
/// The verification flow that was started with this request has finished.
Done,
/// The verification process has been cancelled.
Cancelled {
/// Information about the reason of the cancellation.
cancel_info: CancelInfo,
},
}

impl From<RustVerificationRequestState> for VerificationRequestState {
fn from(value: RustVerificationRequestState) -> Self {
match value {
// The clients do not need to distinguish `Created` and `Requested` state
RustVerificationRequestState::Created { .. } => Self::Requested,
RustVerificationRequestState::Requested { .. } => Self::Requested,
RustVerificationRequestState::Ready { .. } => Self::Ready,
RustVerificationRequestState::Done => Self::Done,
RustVerificationRequestState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
}
}
}

/// The verificatoin request object which then can transition into some concrete
/// verification method
pub struct VerificationRequest {
Expand Down Expand Up @@ -559,7 +670,7 @@ impl VerificationRequest {
Ok(self
.runtime
.block_on(self.inner.generate_qr_code())?
.map(|qr| QrCode { inner: qr }.into()))
.map(|qr| QrCode { inner: qr, runtime: self.runtime.clone() }.into()))
}

/// Pass data from a scanned QR code to an active verification request and
Expand All @@ -585,9 +696,48 @@ impl VerificationRequest {
if let Some(qr) = self.runtime.block_on(self.inner.scan_qr_code(data)).ok()? {
let request = qr.reciprocate()?;

Some(ScanResult { qr: QrCode { inner: qr }.into(), request: request.into() })
Some(ScanResult {
qr: QrCode { inner: qr, runtime: self.runtime.clone() }.into(),
request: request.into(),
})
} else {
None
}
}

/// Set a listener for changes in the verification request
///
/// The given callback will be called whenever the state changes.
pub fn set_changes_listener(&self, listener: Box<dyn VerificationRequestListener>) {
let stream = self.inner.changes();

self.runtime.spawn(Self::changes_listener(stream, listener));
}

/// Get the current state of the verification request.
pub fn state(&self) -> VerificationRequestState {
self.inner.state().into()
}

async fn changes_listener(
mut stream: impl Stream<Item = RustVerificationRequestState> + std::marker::Unpin,
listener: Box<dyn VerificationRequestListener>,
) {
while let Some(state) = stream.next().await {
// If we receive a done or a cancelled state we're at the end of our road, we
// break out of the loop to deallocate the stream and finish the
// task.
let should_break = matches!(
state,
RustVerificationRequestState::Done { .. }
| RustVerificationRequestState::Cancelled { .. }
);

listener.on_change(state.into());

if should_break {
break;
}
}
}
}
4 changes: 2 additions & 2 deletions crates/matrix-sdk-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ pub use requests::{
pub use store::{CrossSigningKeyExport, CryptoStoreError, SecretImportError, SecretInfo};
pub use verification::{
format_emojis, AcceptSettings, AcceptedProtocols, CancelInfo, Emoji, EmojiShortAuthString, Sas,
SasState, Verification, VerificationRequest,
SasState, Verification, VerificationRequest, VerificationRequestState,
};
#[cfg(feature = "qrcode")]
pub use verification::{QrVerification, ScanError};
pub use verification::{QrVerification, QrVerificationState, ScanError};

/// Re-exported Error types from the [vodozemac](https://crates.io/crates/vodozemac) crate.
pub mod vodozemac {
Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk-crypto/src/verification/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use event_enums::OutgoingContent;
pub use machine::VerificationMachine;
use matrix_sdk_common::locks::Mutex;
#[cfg(feature = "qrcode")]
pub use qrcode::{QrVerification, ScanError};
pub use requests::VerificationRequest;
pub use qrcode::{QrVerification, QrVerificationState, ScanError};
pub use requests::{VerificationRequest, VerificationRequestState};
#[cfg(feature = "qrcode")]
use ruma::events::key::verification::done::{
KeyVerificationDoneEventContent, ToDeviceKeyVerificationDoneEventContent,
Expand Down
Loading

0 comments on commit 68ce25a

Please sign in to comment.