atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
bindings/matrix-sdk-crypto-ffi/src/verification.rsRust807 lines29.0 KB
1
use std::sync::Arc;
2
3
use futures_util::{Stream, StreamExt};
4
use matrix_sdk_common::executor::Handle;
5
use 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
};
12
use ruma::events::key::verification::VerificationMethod;
13
use vodozemac::{base64_decode, base64_encode};
14
15
use 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)]
20
pub 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)]
31
pub 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
62
impl 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)]
81
pub struct Verification {
82
    pub(crate) inner: InnerVerification,
83
    pub(crate) runtime: Handle,
84
}
85
86
#[matrix_sdk_ffi_macros::export]
87
impl 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)]
111
pub struct Sas {
112
    pub(crate) inner: Box<InnerSas>,
113
    pub(crate) runtime: Handle,
114
}
115
116
#[matrix_sdk_ffi_macros::export]
117
impl 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
257
impl 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)]
281
pub 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)]
292
pub 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
311
impl 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)]
327
pub struct QrCode {
328
    pub(crate) inner: Box<InnerQr>,
329
    pub(crate) runtime: Handle,
330
}
331
332
#[matrix_sdk_ffi_macros::export]
333
impl 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
439
impl 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)]
464
pub 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
473
impl 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)]
485
pub 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)]
495
pub 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)]
505
pub 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)]
515
pub 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)]
527
pub 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)]
538
pub 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)]
561
pub struct VerificationRequest {
562
    pub(crate) inner: InnerVerificationRequest,
563
    pub(crate) runtime: Handle,
564
}
565
566
#[matrix_sdk_ffi_macros::export]
567
impl 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
744
impl 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
}