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