matrix_sdk_crypto_ffi/verification.rs
1use std::sync::Arc;
2
3use futures_util::{Stream, StreamExt};
4use matrix_sdk_common::executor::Handle;
5use matrix_sdk_crypto::{
6 CancelInfo as RustCancelInfo, QrVerification as InnerQr, QrVerificationState, Sas as InnerSas,
7 SasState as RustSasState, Verification as InnerVerification,
8 VerificationRequest as InnerVerificationRequest,
9 VerificationRequestState as RustVerificationRequestState,
10 matrix_sdk_qrcode::QrVerificationData,
11};
12use ruma::events::key::verification::VerificationMethod;
13use vodozemac::{base64_decode, base64_encode};
14
15use crate::{CryptoStoreError, OutgoingVerificationRequest, SignatureUploadRequest};
16
17/// Listener that will be passed over the FFI to report changes to a SAS
18/// verification.
19#[matrix_sdk_ffi_macros::export(callback_interface)]
20pub trait SasListener: Send {
21 /// The callback that should be called on the Rust side
22 ///
23 /// # Arguments
24 ///
25 /// * `state` - The current state of the SAS verification.
26 fn on_change(&self, state: SasState);
27}
28
29/// An Enum describing the state the SAS verification is in.
30#[derive(uniffi::Enum)]
31pub enum SasState {
32 /// The verification has been created, the protocols that should be used
33 /// have been proposed to the other party.
34 Created,
35 /// The verification has been started, the other party proposed the
36 /// protocols that should be used and that can be accepted.
37 Started,
38 /// The verification has been accepted and both sides agreed to a set of
39 /// protocols that will be used for the verification process.
40 Accepted,
41 /// The public keys have been exchanged and the short auth string can be
42 /// presented to the user.
43 KeysExchanged {
44 /// The emojis that represent the short auth string, will be `None` if
45 /// the emoji SAS method wasn't one of accepted protocols.
46 emojis: Option<Vec<i32>>,
47 /// The list of decimals that represent the short auth string.
48 decimals: Vec<i32>,
49 },
50 /// The verification process has been confirmed from our side, we're waiting
51 /// for the other side to confirm as well.
52 Confirmed,
53 /// The verification process has been successfully concluded.
54 Done,
55 /// The verification process has been cancelled.
56 Cancelled {
57 /// Information about the reason of the cancellation.
58 cancel_info: CancelInfo,
59 },
60}
61
62impl From<RustSasState> for SasState {
63 fn from(s: RustSasState) -> Self {
64 match s {
65 RustSasState::Created { .. } => Self::Created,
66 RustSasState::Started { .. } => Self::Started,
67 RustSasState::Accepted { .. } => Self::Accepted,
68 RustSasState::KeysExchanged { emojis, decimals } => Self::KeysExchanged {
69 emojis: emojis.map(|e| e.indices.map(|i| i as i32).to_vec()),
70 decimals: [decimals.0.into(), decimals.1.into(), decimals.2.into()].to_vec(),
71 },
72 RustSasState::Confirmed => Self::Confirmed,
73 RustSasState::Done { .. } => Self::Done,
74 RustSasState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
75 }
76 }
77}
78
79/// Enum representing the different verification flows we support.
80#[derive(uniffi::Object)]
81pub struct Verification {
82 pub(crate) inner: InnerVerification,
83 pub(crate) runtime: Handle,
84}
85
86#[matrix_sdk_ffi_macros::export]
87impl Verification {
88 /// Try to represent the `Verification` as an `Sas` verification object,
89 /// returns `None` if the verification is not a `Sas` verification.
90 pub fn as_sas(&self) -> Option<Arc<Sas>> {
91 if let InnerVerification::SasV1(sas) = &self.inner {
92 Some(Sas { inner: sas.clone(), runtime: self.runtime.to_owned() }.into())
93 } else {
94 None
95 }
96 }
97
98 /// Try to represent the `Verification` as an `QrCode` verification object,
99 /// returns `None` if the verification is not a `QrCode` verification.
100 pub fn as_qr(&self) -> Option<Arc<QrCode>> {
101 if let InnerVerification::QrV1(qr) = &self.inner {
102 Some(QrCode { inner: qr.clone(), runtime: self.runtime.to_owned() }.into())
103 } else {
104 None
105 }
106 }
107}
108
109/// The `m.sas.v1` verification flow.
110#[derive(uniffi::Object)]
111pub struct Sas {
112 pub(crate) inner: Box<InnerSas>,
113 pub(crate) runtime: Handle,
114}
115
116#[matrix_sdk_ffi_macros::export]
117impl Sas {
118 /// Get the user id of the other side.
119 pub fn other_user_id(&self) -> String {
120 self.inner.other_user_id().to_string()
121 }
122
123 /// Get the device ID of the other side.
124 pub fn other_device_id(&self) -> String {
125 self.inner.other_device_id().to_string()
126 }
127
128 /// Get the unique ID that identifies this SAS verification flow.
129 pub fn flow_id(&self) -> String {
130 self.inner.flow_id().as_str().to_owned()
131 }
132
133 /// Get the room id if the verification is happening inside a room.
134 pub fn room_id(&self) -> Option<String> {
135 self.inner.room_id().map(|r| r.to_string())
136 }
137
138 /// Is the SAS flow done.
139 pub fn is_done(&self) -> bool {
140 self.inner.is_done()
141 }
142
143 /// Did we initiate the verification flow.
144 pub fn we_started(&self) -> bool {
145 self.inner.we_started()
146 }
147
148 /// Accept that we're going forward with the short auth string verification.
149 pub fn accept(&self) -> Option<OutgoingVerificationRequest> {
150 self.inner.accept().map(|r| r.into())
151 }
152
153 /// Confirm a verification was successful.
154 ///
155 /// This method should be called if a short auth string should be confirmed
156 /// as matching.
157 pub fn confirm(&self) -> Result<Option<ConfirmVerificationResult>, CryptoStoreError> {
158 let (requests, signature_request) = self.runtime.block_on(self.inner.confirm())?;
159
160 let requests = requests.into_iter().map(|r| r.into()).collect();
161
162 Ok(Some(ConfirmVerificationResult {
163 requests,
164 signature_request: signature_request.map(|s| s.into()),
165 }))
166 }
167
168 /// Cancel the SAS verification using the given cancel code.
169 ///
170 /// # Arguments
171 ///
172 /// * `cancel_code` - The error code for why the verification was cancelled,
173 /// manual cancellatio usually happens with `m.user` cancel code. The full
174 /// list of cancel codes can be found in the [spec]
175 ///
176 /// [spec]: https://spec.matrix.org/unstable/client-server-api/#mkeyverificationcancel
177 pub fn cancel(&self, cancel_code: String) -> Option<OutgoingVerificationRequest> {
178 self.inner.cancel_with_code(cancel_code.into()).map(|r| r.into())
179 }
180
181 /// Get a list of emoji indices of the emoji representation of the short
182 /// auth string.
183 ///
184 /// *Note*: A SAS verification needs to be started and in the presentable
185 /// state for this to return the list of emoji indices, otherwise returns
186 /// `None`.
187 pub fn get_emoji_indices(&self) -> Option<Vec<i32>> {
188 self.inner.emoji_index().map(|v| v.iter().map(|i| (*i).into()).collect())
189 }
190
191 /// Get the decimal representation of the short auth string.
192 ///
193 /// *Note*: A SAS verification needs to be started and in the presentable
194 /// state for this to return the list of decimals, otherwise returns
195 /// `None`.
196 pub fn get_decimals(&self) -> Option<Vec<i32>> {
197 self.inner.decimals().map(|v| [v.0.into(), v.1.into(), v.2.into()].to_vec())
198 }
199
200 /// Set a listener for changes in the SAS verification process.
201 ///
202 /// The given callback will be called whenever the state changes.
203 ///
204 /// This method can be used to react to changes in the state of the
205 /// verification process, or rather the method can be used to handle
206 /// each step of the verification process.
207 ///
208 /// This method will spawn a tokio task on the Rust side, once we reach the
209 /// Done or Cancelled state, the task will stop listening for changes.
210 ///
211 /// # Flowchart
212 ///
213 /// The flow of the verification process is pictured below. Please note
214 /// that the process can be cancelled at each step of the process.
215 /// Either side can cancel the process.
216 ///
217 /// ```text
218 /// ┌───────┐
219 /// │Started│
220 /// └───┬───┘
221 /// │
222 /// ┌────⌄───┐
223 /// │Accepted│
224 /// └────┬───┘
225 /// │
226 /// ┌───────⌄──────┐
227 /// │Keys Exchanged│
228 /// └───────┬──────┘
229 /// │
230 /// ________⌄________
231 /// ╱ ╲ ┌─────────┐
232 /// ╱ Does the short ╲______│Cancelled│
233 /// ╲ auth string match ╱ no └─────────┘
234 /// ╲_________________╱
235 /// │yes
236 /// │
237 /// ┌────⌄────┐
238 /// │Confirmed│
239 /// └────┬────┘
240 /// │
241 /// ┌───⌄───┐
242 /// │ Done │
243 /// └───────┘
244 /// ```
245 pub fn set_changes_listener(&self, listener: Box<dyn SasListener>) {
246 let stream = self.inner.changes();
247
248 self.runtime.spawn(Self::changes_listener(stream, listener));
249 }
250
251 /// Get the current state of the SAS verification process.
252 pub fn state(&self) -> SasState {
253 self.inner.state().into()
254 }
255}
256
257impl Sas {
258 async fn changes_listener(
259 mut stream: impl Stream<Item = RustSasState> + std::marker::Unpin,
260 listener: Box<dyn SasListener>,
261 ) {
262 while let Some(state) = stream.next().await {
263 // If we receive a done or a cancelled state we're at the end of our road, we
264 // break out of the loop to deallocate the stream and finish the
265 // task.
266 let should_break =
267 matches!(state, RustSasState::Done { .. } | RustSasState::Cancelled { .. });
268
269 listener.on_change(state.into());
270
271 if should_break {
272 break;
273 }
274 }
275 }
276}
277
278/// Listener that will be passed over the FFI to report changes to a QrCode
279/// verification.
280#[matrix_sdk_ffi_macros::export(callback_interface)]
281pub trait QrCodeListener: Send {
282 /// The callback that should be called on the Rust side
283 ///
284 /// # Arguments
285 ///
286 /// * `state` - The current state of the QrCode verification.
287 fn on_change(&self, state: QrCodeState);
288}
289
290/// An Enum describing the state the QrCode verification is in.
291#[derive(uniffi::Enum)]
292pub enum QrCodeState {
293 /// The QR verification has been started.
294 Started,
295 /// The QR verification has been scanned by the other side.
296 Scanned,
297 /// The scanning of the QR code has been confirmed by us.
298 Confirmed,
299 /// We have successfully scanned the QR code and are able to send a
300 /// reciprocation event.
301 Reciprocated,
302 /// The verification process has been successfully concluded.
303 Done,
304 /// The verification process has been cancelled.
305 Cancelled {
306 /// Information about the reason of the cancellation.
307 cancel_info: CancelInfo,
308 },
309}
310
311impl From<QrVerificationState> for QrCodeState {
312 fn from(value: QrVerificationState) -> Self {
313 match value {
314 QrVerificationState::Started => Self::Started,
315 QrVerificationState::Scanned => Self::Scanned,
316 QrVerificationState::Confirmed => Self::Confirmed,
317 QrVerificationState::Reciprocated => Self::Reciprocated,
318 QrVerificationState::Done { .. } => Self::Done,
319 QrVerificationState::Cancelled(c) => Self::Cancelled { cancel_info: c.into() },
320 }
321 }
322}
323
324/// The `m.qr_code.scan.v1`, `m.qr_code.show.v1`, and `m.reciprocate.v1`
325/// verification flow.
326#[derive(uniffi::Object)]
327pub struct QrCode {
328 pub(crate) inner: Box<InnerQr>,
329 pub(crate) runtime: Handle,
330}
331
332#[matrix_sdk_ffi_macros::export]
333impl QrCode {
334 /// Get the user id of the other side.
335 pub fn other_user_id(&self) -> String {
336 self.inner.other_user_id().to_string()
337 }
338
339 /// Get the device ID of the other side.
340 pub fn other_device_id(&self) -> String {
341 self.inner.other_device_id().to_string()
342 }
343
344 /// Get the unique ID that identifies this QR code verification flow.
345 pub fn flow_id(&self) -> String {
346 self.inner.flow_id().as_str().to_owned()
347 }
348
349 /// Get the room id if the verification is happening inside a room.
350 pub fn room_id(&self) -> Option<String> {
351 self.inner.room_id().map(|r| r.to_string())
352 }
353
354 /// Is the QR code verification done.
355 pub fn is_done(&self) -> bool {
356 self.inner.is_done()
357 }
358
359 /// Has the verification flow been cancelled.
360 pub fn is_cancelled(&self) -> bool {
361 self.inner.is_cancelled()
362 }
363
364 /// Did we initiate the verification flow.
365 pub fn we_started(&self) -> bool {
366 self.inner.we_started()
367 }
368
369 /// Get the CancelInfo of this QR code verification object.
370 ///
371 /// Will be `None` if the flow has not been cancelled.
372 pub fn cancel_info(&self) -> Option<CancelInfo> {
373 self.inner.cancel_info().map(|c| c.into())
374 }
375
376 /// Has the QR verification been scanned by the other side.
377 ///
378 /// When the verification object is in this state it's required that the
379 /// user confirms that the other side has scanned the QR code.
380 pub fn has_been_scanned(&self) -> bool {
381 self.inner.has_been_scanned()
382 }
383
384 /// Have we successfully scanned the QR code and are able to send a
385 /// reciprocation event.
386 pub fn reciprocated(&self) -> bool {
387 self.inner.reciprocated()
388 }
389
390 /// Cancel the QR code verification using the given cancel code.
391 ///
392 /// # Arguments
393 ///
394 /// * `cancel_code` - The error code for why the verification was cancelled,
395 /// manual cancellatio usually happens with `m.user` cancel code. The full
396 /// list of cancel codes can be found in the [spec]
397 ///
398 /// [spec]: https://spec.matrix.org/unstable/client-server-api/#mkeyverificationcancel
399 pub fn cancel(&self, cancel_code: String) -> Option<OutgoingVerificationRequest> {
400 self.inner.cancel_with_code(cancel_code.into()).map(|r| r.into())
401 }
402
403 /// Confirm a verification was successful.
404 ///
405 /// This method should be called if we want to confirm that the other side
406 /// has scanned our QR code.
407 pub fn confirm(&self) -> Option<ConfirmVerificationResult> {
408 self.inner.confirm_scanning().map(|r| ConfirmVerificationResult {
409 requests: vec![r.into()],
410 signature_request: None,
411 })
412 }
413
414 /// Generate data that should be encoded as a QR code.
415 ///
416 /// This method should be called right before a QR code should be displayed,
417 /// the returned data is base64 encoded (without padding) and needs to be
418 /// decoded on the other side before it can be put through a QR code
419 /// generator.
420 pub fn generate_qr_code(&self) -> Option<String> {
421 self.inner.to_bytes().map(base64_encode).ok()
422 }
423
424 /// Set a listener for changes in the QrCode verification process.
425 ///
426 /// The given callback will be called whenever the state changes.
427 pub fn set_changes_listener(&self, listener: Box<dyn QrCodeListener>) {
428 let stream = self.inner.changes();
429
430 self.runtime.spawn(Self::changes_listener(stream, listener));
431 }
432
433 /// Get the current state of the QrCode verification process.
434 pub fn state(&self) -> QrCodeState {
435 self.inner.state().into()
436 }
437}
438
439impl QrCode {
440 async fn changes_listener(
441 mut stream: impl Stream<Item = QrVerificationState> + std::marker::Unpin,
442 listener: Box<dyn QrCodeListener>,
443 ) {
444 while let Some(state) = stream.next().await {
445 // If we receive a done or a cancelled state we're at the end of our road, we
446 // break out of the loop to deallocate the stream and finish the
447 // task.
448 let should_break = matches!(
449 state,
450 QrVerificationState::Done { .. } | QrVerificationState::Cancelled { .. }
451 );
452
453 listener.on_change(state.into());
454
455 if should_break {
456 break;
457 }
458 }
459 }
460}
461
462/// Information on why a verification flow has been cancelled and by whom.
463#[derive(uniffi::Record)]
464pub struct CancelInfo {
465 /// The textual representation of the cancel reason
466 pub reason: String,
467 /// The code describing the cancel reason
468 pub cancel_code: String,
469 /// Was the verification flow cancelled by us
470 pub cancelled_by_us: bool,
471}
472
473impl From<RustCancelInfo> for CancelInfo {
474 fn from(c: RustCancelInfo) -> Self {
475 Self {
476 reason: c.reason().to_owned(),
477 cancel_code: c.cancel_code().to_string(),
478 cancelled_by_us: c.cancelled_by_us(),
479 }
480 }
481}
482
483/// A result type for starting SAS verifications.
484#[derive(uniffi::Record)]
485pub struct StartSasResult {
486 /// The SAS verification object that got created.
487 pub sas: Arc<Sas>,
488 /// The request that needs to be sent out to notify the other side that a
489 /// SAS verification should start.
490 pub request: OutgoingVerificationRequest,
491}
492
493/// A result type for scanning QR codes.
494#[derive(uniffi::Record)]
495pub struct ScanResult {
496 /// The QR code verification object that got created.
497 pub qr: Arc<QrCode>,
498 /// The request that needs to be sent out to notify the other side that a
499 /// QR code verification should start.
500 pub request: OutgoingVerificationRequest,
501}
502
503/// A result type for requesting verifications.
504#[derive(uniffi::Record)]
505pub struct RequestVerificationResult {
506 /// The verification request object that got created.
507 pub verification: Arc<VerificationRequest>,
508 /// The request that needs to be sent out to notify the other side that
509 /// we're requesting verification to begin.
510 pub request: OutgoingVerificationRequest,
511}
512
513/// A result type for confirming verifications.
514#[derive(uniffi::Record)]
515pub struct ConfirmVerificationResult {
516 /// The requests that needs to be sent out to notify the other side that we
517 /// confirmed the verification.
518 pub requests: Vec<OutgoingVerificationRequest>,
519 /// A request that will upload signatures of the verified device or user, if
520 /// the verification is completed and we're able to sign devices or users
521 pub signature_request: Option<SignatureUploadRequest>,
522}
523
524/// Listener that will be passed over the FFI to report changes to a
525/// verification request.
526#[matrix_sdk_ffi_macros::export(callback_interface)]
527pub trait VerificationRequestListener: Send {
528 /// The callback that should be called on the Rust side
529 ///
530 /// # Arguments
531 ///
532 /// * `state` - The current state of the verification request.
533 fn on_change(&self, state: VerificationRequestState);
534}
535
536/// An Enum describing the state the QrCode verification is in.
537#[derive(uniffi::Enum)]
538pub enum VerificationRequestState {
539 /// The verification request was sent
540 Requested,
541 /// The verification request is ready to start a verification flow.
542 Ready {
543 /// The verification methods supported by the other side.
544 their_methods: Vec<String>,
545
546 /// The verification methods supported by the us.
547 our_methods: Vec<String>,
548 },
549 /// The verification flow that was started with this request has finished.
550 Done,
551 /// The verification process has been cancelled.
552 Cancelled {
553 /// Information about the reason of the cancellation.
554 cancel_info: CancelInfo,
555 },
556}
557
558/// The verificatoin request object which then can transition into some concrete
559/// verification method
560#[derive(uniffi::Object)]
561pub struct VerificationRequest {
562 pub(crate) inner: InnerVerificationRequest,
563 pub(crate) runtime: Handle,
564}
565
566#[matrix_sdk_ffi_macros::export]
567impl VerificationRequest {
568 /// The id of the other user that is participating in this verification
569 /// request.
570 pub fn other_user_id(&self) -> String {
571 self.inner.other_user().to_string()
572 }
573
574 /// The id of the other device that is participating in this verification.
575 pub fn other_device_id(&self) -> Option<String> {
576 self.inner.other_device_id().map(|d| d.to_string())
577 }
578
579 /// Get the unique ID of this verification request
580 pub fn flow_id(&self) -> String {
581 self.inner.flow_id().as_str().to_owned()
582 }
583
584 /// Get the room id if the verification is happening inside a room.
585 pub fn room_id(&self) -> Option<String> {
586 self.inner.room_id().map(|r| r.to_string())
587 }
588
589 /// Has the verification flow that was started with this request finished.
590 pub fn is_done(&self) -> bool {
591 self.inner.is_done()
592 }
593
594 /// Is the verification request ready to start a verification flow.
595 pub fn is_ready(&self) -> bool {
596 self.inner.is_ready()
597 }
598
599 /// Did we initiate the verification request
600 pub fn we_started(&self) -> bool {
601 self.inner.we_started()
602 }
603
604 /// Has the verification request been answered by another device.
605 pub fn is_passive(&self) -> bool {
606 self.inner.is_passive()
607 }
608
609 /// Has the verification flow that been cancelled.
610 pub fn is_cancelled(&self) -> bool {
611 self.inner.is_cancelled()
612 }
613
614 /// Get info about the cancellation if the verification request has been
615 /// cancelled.
616 pub fn cancel_info(&self) -> Option<CancelInfo> {
617 self.inner.cancel_info().map(|v| v.into())
618 }
619
620 /// Get the supported verification methods of the other side.
621 ///
622 /// Will be present only if the other side requested the verification or if
623 /// we're in the ready state.
624 pub fn their_supported_methods(&self) -> Option<Vec<String>> {
625 self.inner.their_supported_methods().map(|m| m.iter().map(|m| m.to_string()).collect())
626 }
627
628 /// Get our own supported verification methods that we advertised.
629 ///
630 /// Will be present only we requested the verification or if we're in the
631 /// ready state.
632 pub fn our_supported_methods(&self) -> Option<Vec<String>> {
633 self.inner.our_supported_methods().map(|m| m.iter().map(|m| m.to_string()).collect())
634 }
635
636 /// Accept a verification requests that we share with the given user with
637 /// the given flow id.
638 ///
639 /// This will move the verification request into the ready state.
640 ///
641 /// # Arguments
642 ///
643 /// * `user_id` - The ID of the user for which we would like to accept the
644 /// verification requests.
645 ///
646 /// * `flow_id` - The ID that uniquely identifies the verification flow.
647 ///
648 /// * `methods` - A list of verification methods that we want to advertise
649 /// as supported.
650 pub fn accept(&self, methods: Vec<String>) -> Option<OutgoingVerificationRequest> {
651 let methods = methods.into_iter().map(VerificationMethod::from).collect();
652 self.inner.accept_with_methods(methods).map(|r| r.into())
653 }
654
655 /// Cancel a verification for the given user with the given flow id using
656 /// the given cancel code.
657 pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
658 self.inner.cancel().map(|r| r.into())
659 }
660
661 /// Transition from a verification request into short auth string based
662 /// verification.
663 ///
664 /// # Arguments
665 ///
666 /// * `user_id` - The ID of the user for which we would like to start the
667 /// SAS verification.
668 ///
669 /// * `flow_id` - The ID of the verification request that initiated the
670 /// verification flow.
671 pub fn start_sas_verification(&self) -> Result<Option<StartSasResult>, CryptoStoreError> {
672 Ok(self.runtime.block_on(self.inner.start_sas())?.map(|(sas, r)| StartSasResult {
673 sas: Arc::new(Sas { inner: Box::new(sas), runtime: self.runtime.clone() }),
674 request: r.into(),
675 }))
676 }
677
678 /// Transition from a verification request into QR code verification.
679 ///
680 /// This method should be called when one wants to display a QR code so the
681 /// other side can scan it and move the QR code verification forward.
682 ///
683 /// # Arguments
684 ///
685 /// * `user_id` - The ID of the user for which we would like to start the QR
686 /// code verification.
687 ///
688 /// * `flow_id` - The ID of the verification request that initiated the
689 /// verification flow.
690 pub fn start_qr_verification(&self) -> Result<Option<Arc<QrCode>>, CryptoStoreError> {
691 Ok(self
692 .runtime
693 .block_on(self.inner.generate_qr_code())?
694 .map(|qr| QrCode { inner: Box::new(qr), runtime: self.runtime.clone() }.into()))
695 }
696
697 /// Pass data from a scanned QR code to an active verification request and
698 /// transition into QR code verification.
699 ///
700 /// This requires an active `VerificationRequest` to succeed, returns `None`
701 /// if no `VerificationRequest` is found or if the QR code data is invalid.
702 ///
703 /// # Arguments
704 ///
705 /// * `user_id` - The ID of the user for which we would like to start the QR
706 /// code verification.
707 ///
708 /// * `flow_id` - The ID of the verification request that initiated the
709 /// verification flow.
710 ///
711 /// * `data` - The data that was extracted from the scanned QR code as an
712 /// base64 encoded string, without padding.
713 pub fn scan_qr_code(&self, data: String) -> Option<ScanResult> {
714 let data = base64_decode(data).ok()?;
715 let data = QrVerificationData::from_bytes(data).ok()?;
716
717 if let Some(qr) = self.runtime.block_on(self.inner.scan_qr_code(data)).ok()? {
718 let request = qr.reciprocate()?;
719
720 Some(ScanResult {
721 qr: QrCode { inner: Box::new(qr), runtime: self.runtime.clone() }.into(),
722 request: request.into(),
723 })
724 } else {
725 None
726 }
727 }
728
729 /// Set a listener for changes in the verification request
730 ///
731 /// The given callback will be called whenever the state changes.
732 pub fn set_changes_listener(&self, listener: Box<dyn VerificationRequestListener>) {
733 let stream = self.inner.changes();
734
735 self.runtime.spawn(Self::changes_listener(self.inner.to_owned(), stream, listener));
736 }
737
738 /// Get the current state of the verification request.
739 pub fn state(&self) -> VerificationRequestState {
740 Self::convert_verification_request(&self.inner, self.inner.state())
741 }
742}
743
744impl VerificationRequest {
745 fn convert_verification_request(
746 request: &InnerVerificationRequest,
747 value: RustVerificationRequestState,
748 ) -> VerificationRequestState {
749 match value {
750 // The clients do not need to distinguish `Created` and `Requested` state
751 RustVerificationRequestState::Created { .. } => VerificationRequestState::Requested,
752 RustVerificationRequestState::Requested { .. } => VerificationRequestState::Requested,
753 RustVerificationRequestState::Ready {
754 their_methods,
755 our_methods,
756 other_device_data: _,
757 } => VerificationRequestState::Ready {
758 their_methods: their_methods.iter().map(|m| m.to_string()).collect(),
759 our_methods: our_methods.iter().map(|m| m.to_string()).collect(),
760 },
761 RustVerificationRequestState::Done => VerificationRequestState::Done,
762 RustVerificationRequestState::Transitioned { .. } => {
763 let their_methods = request
764 .their_supported_methods()
765 .expect("The transitioned state should know the other side's methods")
766 .into_iter()
767 .map(|m| m.to_string())
768 .collect();
769 let our_methods = request
770 .our_supported_methods()
771 .expect("The transitioned state should know our own supported methods")
772 .iter()
773 .map(|m| m.to_string())
774 .collect();
775 VerificationRequestState::Ready { their_methods, our_methods }
776 }
777
778 RustVerificationRequestState::Cancelled(c) => {
779 VerificationRequestState::Cancelled { cancel_info: c.into() }
780 }
781 }
782 }
783
784 async fn changes_listener(
785 request: InnerVerificationRequest,
786 mut stream: impl Stream<Item = RustVerificationRequestState> + std::marker::Unpin,
787 listener: Box<dyn VerificationRequestListener>,
788 ) {
789 while let Some(state) = stream.next().await {
790 // If we receive a done or a cancelled state we're at the end of our road, we
791 // break out of the loop to deallocate the stream and finish the
792 // task.
793 let should_break = matches!(
794 state,
795 RustVerificationRequestState::Done | RustVerificationRequestState::Cancelled { .. }
796 );
797
798 let state = Self::convert_verification_request(&request, state);
799
800 listener.on_change(state);
801
802 if should_break {
803 break;
804 }
805 }
806 }
807}