Skip to content

Commit 9239470

Browse files
authored
feat(crypto): Add signalling to the verification requests and qr code verification
1 parent dab2063 commit 9239470

File tree

8 files changed

+484
-113
lines changed

8 files changed

+484
-113
lines changed

bindings/matrix-sdk-crypto-ffi/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ use ruma::{
4545
use serde::{Deserialize, Serialize};
4646
pub use users::UserIdentity;
4747
pub use verification::{
48-
CancelInfo, ConfirmVerificationResult, QrCode, RequestVerificationResult, Sas, SasListener,
49-
SasState, ScanResult, StartSasResult, Verification, VerificationRequest,
48+
CancelInfo, ConfirmVerificationResult, QrCode, QrCodeListener, QrCodeState,
49+
RequestVerificationResult, Sas, SasListener, SasState, ScanResult, StartSasResult,
50+
Verification, VerificationRequest, VerificationRequestListener, VerificationRequestState,
5051
};
5152

5253
/// Struct collecting data that is important to migrate to the rust-sdk

bindings/matrix-sdk-crypto-ffi/src/olm.udl

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ interface Sas {
157157
sequence<i32>? get_emoji_indices();
158158
sequence<i32>? get_decimals();
159159

160-
void set_changes_listener(SasListener callback);
160+
void set_changes_listener(SasListener listener);
161161
SasState state();
162162
};
163163

@@ -196,6 +196,23 @@ interface QrCode {
196196
ConfirmVerificationResult? confirm();
197197
OutgoingVerificationRequest? cancel([ByRef] string cancel_code);
198198
string? generate_qr_code();
199+
200+
void set_changes_listener(QrCodeListener listener);
201+
QrCodeState state();
202+
};
203+
204+
[Enum]
205+
interface QrCodeState {
206+
Started();
207+
Scanned();
208+
Confirmed();
209+
Reciprocated();
210+
Done();
211+
Cancelled(CancelInfo cancel_info);
212+
};
213+
214+
callback interface QrCodeListener {
215+
void on_change(QrCodeState state);
199216
};
200217

201218
interface VerificationRequest {
@@ -223,6 +240,21 @@ interface VerificationRequest {
223240
ScanResult? scan_qr_code([ByRef] string data);
224241

225242
OutgoingVerificationRequest? cancel();
243+
244+
void set_changes_listener(VerificationRequestListener listener);
245+
VerificationRequestState state();
246+
};
247+
248+
[Enum]
249+
interface VerificationRequestState {
250+
Requested();
251+
Ready(sequence<string> their_methods, sequence<string> our_methods);
252+
Done();
253+
Cancelled(CancelInfo cancel_info);
254+
};
255+
256+
callback interface VerificationRequestListener {
257+
void on_change(VerificationRequestState state);
226258
};
227259

228260
dictionary RequestVerificationResult {

bindings/matrix-sdk-crypto-ffi/src/verification.rs

Lines changed: 183 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ use base64::{decode_config, encode_config, STANDARD_NO_PAD};
44
use futures_util::{Stream, StreamExt};
55
use matrix_sdk_crypto::{
66
matrix_sdk_qrcode::QrVerificationData, CancelInfo as RustCancelInfo, QrVerification as InnerQr,
7-
Sas as InnerSas, SasState as RustSasState, Verification as InnerVerification,
8-
VerificationRequest as InnerVerificationRequest,
7+
QrVerificationState, Sas as InnerSas, SasState as RustSasState,
8+
Verification as InnerVerification, VerificationRequest as InnerVerificationRequest,
9+
VerificationRequestState as RustVerificationRequestState,
910
};
1011
use ruma::events::key::verification::VerificationMethod;
1112
use tokio::runtime::Handle;
1213

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

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

26-
impl<T: Fn(SasState)> SasListener for T
27-
where
28-
T: Send,
29-
{
30-
fn on_change(&self, state: SasState) {
31-
self(state)
32-
}
33-
}
34-
3527
/// An Enum describing the state the SAS verification is in.
3628
pub enum SasState {
3729
/// The verification has been started, the protocols that should be used
@@ -98,7 +90,7 @@ impl Verification {
9890
/// returns `None` if the verification is not a `QrCode` verification.
9991
pub fn as_qr(&self) -> Option<Arc<QrCode>> {
10092
if let InnerVerification::QrV1(qr) = &self.inner {
101-
Some(QrCode { inner: qr.to_owned() }.into())
93+
Some(QrCode { inner: qr.to_owned(), runtime: self.runtime.to_owned() }.into())
10294
} else {
10395
None
10496
}
@@ -239,20 +231,20 @@ impl Sas {
239231
/// │ Done │
240232
/// └───────┘
241233
/// ```
242-
pub fn set_changes_listener(&self, callback: Box<dyn SasListener>) {
234+
pub fn set_changes_listener(&self, listener: Box<dyn SasListener>) {
243235
let stream = self.inner.changes();
244236

245-
self.runtime.spawn(Self::changes_callback(stream, callback));
237+
self.runtime.spawn(Self::changes_listener(stream, listener));
246238
}
247239

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

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

264-
callback.on_change(state.into());
256+
listener.on_change(state.into());
265257

266258
if should_break {
267259
break;
@@ -270,10 +262,55 @@ impl Sas {
270262
}
271263
}
272264

265+
/// Listener that will be passed over the FFI to report changes to a QrCode
266+
/// verification.
267+
pub trait QrCodeListener: Send {
268+
/// The callback that should be called on the Rust side
269+
///
270+
/// # Arguments
271+
///
272+
/// * `state` - The current state of the QrCode verification.
273+
fn on_change(&self, state: QrCodeState);
274+
}
275+
276+
/// An Enum describing the state the QrCode verification is in.
277+
pub enum QrCodeState {
278+
/// The QR verification has been started.
279+
Started,
280+
/// The QR verification has been scanned by the other side.
281+
Scanned,
282+
/// The scanning of the QR code has been confirmed by us.
283+
Confirmed,
284+
/// We have successfully scanned the QR code and are able to send a
285+
/// reciprocation event.
286+
Reciprocated,
287+
/// The verification process has been successfully concluded.
288+
Done,
289+
/// The verification process has been cancelled.
290+
Cancelled {
291+
/// Information about the reason of the cancellation.
292+
cancel_info: CancelInfo,
293+
},
294+
}
295+
296+
impl From<QrVerificationState> for QrCodeState {
297+
fn from(value: QrVerificationState) -> Self {
298+
match value {
299+
QrVerificationState::Started => Self::Started,
300+
QrVerificationState::Scanned => Self::Scanned,
301+
QrVerificationState::Confirmed => Self::Confirmed,
302+
QrVerificationState::Reciprocated => Self::Reciprocated,
303+
QrVerificationState::Done { .. } => Self::Done,
304+
QrVerificationState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
305+
}
306+
}
307+
}
308+
273309
/// The `m.qr_code.scan.v1`, `m.qr_code.show.v1`, and `m.reciprocate.v1`
274310
/// verification flow.
275311
pub struct QrCode {
276312
pub(crate) inner: InnerQr,
313+
pub(crate) runtime: Handle,
277314
}
278315

279316
impl QrCode {
@@ -366,6 +403,41 @@ impl QrCode {
366403
pub fn generate_qr_code(&self) -> Option<String> {
367404
self.inner.to_bytes().map(|data| encode_config(data, STANDARD_NO_PAD)).ok()
368405
}
406+
407+
/// Set a listener for changes in the QrCode verification process.
408+
///
409+
/// The given callback will be called whenever the state changes.
410+
pub fn set_changes_listener(&self, listener: Box<dyn QrCodeListener>) {
411+
let stream = self.inner.changes();
412+
413+
self.runtime.spawn(Self::changes_listener(stream, listener));
414+
}
415+
416+
/// Get the current state of the QrCode verification process.
417+
pub fn state(&self) -> QrCodeState {
418+
self.inner.state().into()
419+
}
420+
421+
async fn changes_listener(
422+
mut stream: impl Stream<Item = QrVerificationState> + std::marker::Unpin,
423+
listener: Box<dyn QrCodeListener>,
424+
) {
425+
while let Some(state) = stream.next().await {
426+
// If we receive a done or a cancelled state we're at the end of our road, we
427+
// break out of the loop to deallocate the stream and finish the
428+
// task.
429+
let should_break = matches!(
430+
state,
431+
QrVerificationState::Done { .. } | QrVerificationState::Cancelled { .. }
432+
);
433+
434+
listener.on_change(state.into());
435+
436+
if should_break {
437+
break;
438+
}
439+
}
440+
}
369441
}
370442

371443
/// Information on why a verification flow has been cancelled and by whom.
@@ -425,6 +497,58 @@ pub struct ConfirmVerificationResult {
425497
pub signature_request: Option<SignatureUploadRequest>,
426498
}
427499

500+
/// Listener that will be passed over the FFI to report changes to a
501+
/// verification request.
502+
pub trait VerificationRequestListener: Send {
503+
/// The callback that should be called on the Rust side
504+
///
505+
/// # Arguments
506+
///
507+
/// * `state` - The current state of the verification request.
508+
fn on_change(&self, state: VerificationRequestState);
509+
}
510+
511+
/// An Enum describing the state the QrCode verification is in.
512+
pub enum VerificationRequestState {
513+
/// The verification request was sent
514+
Requested,
515+
/// The verification request is ready to start a verification flow.
516+
Ready {
517+
/// The verification methods supported by the other side.
518+
their_methods: Vec<String>,
519+
520+
/// The verification methods supported by the us.
521+
our_methods: Vec<String>,
522+
},
523+
/// The verification flow that was started with this request has finished.
524+
Done,
525+
/// The verification process has been cancelled.
526+
Cancelled {
527+
/// Information about the reason of the cancellation.
528+
cancel_info: CancelInfo,
529+
},
530+
}
531+
532+
impl From<RustVerificationRequestState> for VerificationRequestState {
533+
fn from(value: RustVerificationRequestState) -> Self {
534+
match value {
535+
// The clients do not need to distinguish `Created` and `Requested` state
536+
RustVerificationRequestState::Created { .. } => Self::Requested,
537+
RustVerificationRequestState::Requested { .. } => Self::Requested,
538+
RustVerificationRequestState::Ready {
539+
their_methods,
540+
our_methods,
541+
other_device_id: _,
542+
} => Self::Ready {
543+
their_methods: their_methods.iter().map(|m| m.to_string()).collect(),
544+
our_methods: our_methods.iter().map(|m| m.to_string()).collect(),
545+
},
546+
RustVerificationRequestState::Done => Self::Done,
547+
RustVerificationRequestState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
548+
}
549+
}
550+
}
551+
428552
/// The verificatoin request object which then can transition into some concrete
429553
/// verification method
430554
pub struct VerificationRequest {
@@ -559,7 +683,7 @@ impl VerificationRequest {
559683
Ok(self
560684
.runtime
561685
.block_on(self.inner.generate_qr_code())?
562-
.map(|qr| QrCode { inner: qr }.into()))
686+
.map(|qr| QrCode { inner: qr, runtime: self.runtime.clone() }.into()))
563687
}
564688

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

588-
Some(ScanResult { qr: QrCode { inner: qr }.into(), request: request.into() })
712+
Some(ScanResult {
713+
qr: QrCode { inner: qr, runtime: self.runtime.clone() }.into(),
714+
request: request.into(),
715+
})
589716
} else {
590717
None
591718
}
592719
}
720+
721+
/// Set a listener for changes in the verification request
722+
///
723+
/// The given callback will be called whenever the state changes.
724+
pub fn set_changes_listener(&self, listener: Box<dyn VerificationRequestListener>) {
725+
let stream = self.inner.changes();
726+
727+
self.runtime.spawn(Self::changes_listener(stream, listener));
728+
}
729+
730+
/// Get the current state of the verification request.
731+
pub fn state(&self) -> VerificationRequestState {
732+
self.inner.state().into()
733+
}
734+
735+
async fn changes_listener(
736+
mut stream: impl Stream<Item = RustVerificationRequestState> + std::marker::Unpin,
737+
listener: Box<dyn VerificationRequestListener>,
738+
) {
739+
while let Some(state) = stream.next().await {
740+
// If we receive a done or a cancelled state we're at the end of our road, we
741+
// break out of the loop to deallocate the stream and finish the
742+
// task.
743+
let should_break = matches!(
744+
state,
745+
RustVerificationRequestState::Done { .. }
746+
| RustVerificationRequestState::Cancelled { .. }
747+
);
748+
749+
listener.on_change(state.into());
750+
751+
if should_break {
752+
break;
753+
}
754+
}
755+
}
593756
}

crates/matrix-sdk-crypto/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ pub use requests::{
8989
pub use store::{CrossSigningKeyExport, CryptoStoreError, SecretImportError, SecretInfo};
9090
pub use verification::{
9191
format_emojis, AcceptSettings, AcceptedProtocols, CancelInfo, Emoji, EmojiShortAuthString, Sas,
92-
SasState, Verification, VerificationRequest,
92+
SasState, Verification, VerificationRequest, VerificationRequestState,
9393
};
9494
#[cfg(feature = "qrcode")]
95-
pub use verification::{QrVerification, ScanError};
95+
pub use verification::{QrVerification, QrVerificationState, ScanError};
9696

9797
/// Re-exported Error types from the [vodozemac](https://crates.io/crates/vodozemac) crate.
9898
pub mod vodozemac {

crates/matrix-sdk-crypto/src/verification/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use event_enums::OutgoingContent;
2626
pub use machine::VerificationMachine;
2727
use matrix_sdk_common::locks::Mutex;
2828
#[cfg(feature = "qrcode")]
29-
pub use qrcode::{QrVerification, ScanError};
30-
pub use requests::VerificationRequest;
29+
pub use qrcode::{QrVerification, QrVerificationState, ScanError};
30+
pub use requests::{VerificationRequest, VerificationRequestState};
3131
#[cfg(feature = "qrcode")]
3232
use ruma::events::key::verification::done::{
3333
KeyVerificationDoneEventContent, ToDeviceKeyVerificationDoneEventContent,

0 commit comments

Comments
 (0)