matrix_sdk_crypto/verification/
machine.rs

1// Copyright 2020 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{collections::HashMap, sync::Arc};
16
17use matrix_sdk_common::locks::RwLock as StdRwLock;
18use ruma::{
19    DeviceId, EventId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, RoomId,
20    SecondsSinceUnixEpoch, TransactionId, UInt, UserId,
21    events::{
22        AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
23        key::verification::VerificationMethod,
24    },
25    serde::Raw,
26    uint,
27};
28use tokio::sync::Mutex;
29use tracing::{Span, debug, info, instrument, trace, warn};
30
31use super::{
32    FlowId, Verification, VerificationResult, VerificationStore,
33    cache::{RequestInfo, VerificationCache},
34    event_enums::{AnyEvent, AnyVerificationContent, OutgoingContent},
35    requests::VerificationRequest,
36    sas::Sas,
37};
38use crate::{
39    DeviceData, OtherUserIdentityData,
40    olm::{PrivateCrossSigningIdentity, StaticAccountData},
41    store::{CryptoStoreError, CryptoStoreWrapper},
42    types::requests::{
43        OutgoingRequest, OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest,
44    },
45};
46
47#[derive(Clone, Debug)]
48pub struct VerificationMachine {
49    pub(crate) store: VerificationStore,
50    verifications: VerificationCache,
51    requests: Arc<StdRwLock<HashMap<OwnedUserId, HashMap<String, VerificationRequest>>>>,
52}
53
54impl VerificationMachine {
55    pub(crate) fn new(
56        account: StaticAccountData,
57        identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
58        store: Arc<CryptoStoreWrapper>,
59    ) -> Self {
60        Self {
61            store: VerificationStore { account, private_identity: identity, inner: store },
62            verifications: VerificationCache::new(),
63            requests: Default::default(),
64        }
65    }
66
67    pub(crate) fn own_user_id(&self) -> &UserId {
68        &self.store.account.user_id
69    }
70
71    pub(crate) fn own_device_id(&self) -> &DeviceId {
72        &self.store.account.device_id
73    }
74
75    pub(crate) fn request_to_device_verification(
76        &self,
77        user_id: &UserId,
78        recipient_devices: Vec<OwnedDeviceId>,
79        methods: Option<Vec<VerificationMethod>>,
80    ) -> (VerificationRequest, OutgoingVerificationRequest) {
81        let flow_id = FlowId::from(TransactionId::new());
82
83        let verification = VerificationRequest::new(
84            self.verifications.clone(),
85            self.store.clone(),
86            flow_id,
87            user_id,
88            recipient_devices,
89            methods,
90        );
91
92        self.insert_request(verification.clone());
93
94        let request = verification.request_to_device();
95
96        (verification, request.into())
97    }
98
99    pub fn request_verification(
100        &self,
101        identity: &OtherUserIdentityData,
102        room_id: &RoomId,
103        request_event_id: &EventId,
104        methods: Option<Vec<VerificationMethod>>,
105    ) -> VerificationRequest {
106        let flow_id = FlowId::InRoom(room_id.to_owned(), request_event_id.to_owned());
107
108        let request = VerificationRequest::new(
109            self.verifications.clone(),
110            self.store.clone(),
111            flow_id,
112            identity.user_id(),
113            vec![],
114            methods,
115        );
116
117        self.insert_request(request.clone());
118
119        request
120    }
121
122    pub async fn start_sas(
123        &self,
124        device: DeviceData,
125    ) -> Result<(Sas, OutgoingVerificationRequest), CryptoStoreError> {
126        let identities = self.store.get_identities(device.clone()).await?;
127        let (sas, content) = Sas::start(identities, TransactionId::new(), true, None, None);
128
129        let request = match content {
130            OutgoingContent::Room(r, c) => {
131                RoomMessageRequest { room_id: r, txn_id: TransactionId::new(), content: c }.into()
132            }
133            OutgoingContent::ToDevice(c) => {
134                let request = ToDeviceRequest::with_id(
135                    device.user_id(),
136                    device.device_id().to_owned(),
137                    &c,
138                    TransactionId::new(),
139                );
140
141                self.verifications.insert_sas(sas.clone());
142
143                request.into()
144            }
145        };
146
147        Ok((sas, request))
148    }
149
150    pub fn get_request(
151        &self,
152        user_id: &UserId,
153        flow_id: impl AsRef<str>,
154    ) -> Option<VerificationRequest> {
155        self.requests.read().get(user_id)?.get(flow_id.as_ref()).cloned()
156    }
157
158    pub fn get_requests(&self, user_id: &UserId) -> Vec<VerificationRequest> {
159        self.requests.read().get(user_id).map(|v| v.values().cloned().collect()).unwrap_or_default()
160    }
161
162    /// Add a new `VerificationRequest` object to the cache.
163    /// If there are any existing requests with this user (and different
164    /// flow_id), both the existing and new request will be cancelled.
165    fn insert_request(&self, request: VerificationRequest) {
166        if let Some(r) = self.get_request(request.other_user(), request.flow_id().as_str()) {
167            debug!(flow_id = r.flow_id().as_str(), "Ignoring known verification request",);
168            return;
169        }
170
171        let mut requests = self.requests.write();
172        let user_requests = requests.entry(request.other_user().to_owned()).or_default();
173
174        // Cancel all the old verifications requests as well as the new one we
175        // have for this user if someone tries to have two verifications going
176        // on at once.
177        for old_verification in user_requests.values_mut() {
178            if !old_verification.is_cancelled() {
179                warn!(
180                    "Received a new verification request whilst another request \
181                    with the same user is ongoing. Cancelling both requests."
182                );
183
184                if let Some(r) = old_verification.cancel() {
185                    self.verifications.add_request(r.into())
186                }
187
188                if let Some(r) = request.cancel() {
189                    self.verifications.add_request(r.into())
190                }
191            }
192        }
193
194        // We still want to add the new verification request, in case users
195        // want to inspect the verification object a matching
196        // `m.key.verification.request` produced.
197        user_requests.insert(request.flow_id().as_str().to_owned(), request);
198    }
199
200    pub fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option<Verification> {
201        self.verifications.get(user_id, flow_id)
202    }
203
204    pub fn get_sas(&self, user_id: &UserId, flow_id: &str) -> Option<Box<Sas>> {
205        self.verifications.get_sas(user_id, flow_id)
206    }
207
208    fn is_timestamp_valid(timestamp: MilliSecondsSinceUnixEpoch) -> bool {
209        // The event should be ignored if the event is older than 10 minutes
210        let old_timestamp_threshold: UInt = uint!(600);
211        // The event should be ignored if the event is 5 minutes or more into the
212        // future.
213        let timestamp_threshold: UInt = uint!(300);
214
215        let timestamp = timestamp.as_secs();
216        let now = SecondsSinceUnixEpoch::now().get();
217
218        !(now.saturating_sub(timestamp) > old_timestamp_threshold
219            || timestamp.saturating_sub(now) > timestamp_threshold)
220    }
221
222    fn queue_up_content(
223        &self,
224        recipient: &UserId,
225        recipient_device: &DeviceId,
226        content: OutgoingContent,
227        request_id: Option<RequestInfo>,
228    ) {
229        self.verifications.queue_up_content(recipient, recipient_device, content, request_id)
230    }
231
232    pub fn mark_request_as_sent(&self, request_id: &TransactionId) {
233        self.verifications.mark_request_as_sent(request_id);
234    }
235
236    pub fn outgoing_messages(&self) -> Vec<OutgoingRequest> {
237        self.verifications.outgoing_requests()
238    }
239
240    pub fn garbage_collect(&self) -> Vec<Raw<AnyToDeviceEvent>> {
241        let mut events = vec![];
242
243        let mut requests: Vec<OutgoingVerificationRequest> = {
244            let mut requests = self.requests.write();
245
246            for user_verification in requests.values_mut() {
247                user_verification.retain(|_, v| !(v.is_done() || v.is_cancelled()));
248            }
249            requests.retain(|_, v| !v.is_empty());
250
251            requests.values().flatten().filter_map(|(_, v)| v.cancel_if_timed_out()).collect()
252        };
253
254        requests.extend(self.verifications.garbage_collect());
255
256        for request in requests {
257            if let Ok(OutgoingContent::ToDevice(to_device)) = request.clone().try_into()
258                && let AnyToDeviceEventContent::KeyVerificationCancel(content) = *to_device
259            {
260                let event = ToDeviceEvent::new(self.own_user_id().to_owned(), content);
261
262                events.push(
263                    Raw::new(&event)
264                        .expect("Failed to serialize m.key_verification.cancel event")
265                        .cast(),
266                );
267            }
268
269            self.verifications.add_verification_request(request)
270        }
271
272        events
273    }
274
275    async fn mark_sas_as_done(
276        &self,
277        sas: &Sas,
278        out_content: Option<OutgoingContent>,
279    ) -> Result<(), CryptoStoreError> {
280        match sas.mark_as_done().await? {
281            VerificationResult::Ok => {
282                if let Some(c) = out_content {
283                    self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c, None);
284                }
285            }
286            VerificationResult::Cancel(c) => {
287                if let Some(r) = sas.cancel_with_code(c) {
288                    self.verifications.add_request(r.into());
289                }
290            }
291            VerificationResult::SignatureUpload(r) => {
292                self.verifications.add_request(r.into());
293
294                if let Some(c) = out_content {
295                    self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c, None);
296                }
297            }
298        }
299
300        Ok(())
301    }
302
303    #[instrument(skip_all, fields(flow_id))]
304    pub async fn receive_any_event(
305        &self,
306        event: impl Into<AnyEvent<'_>>,
307    ) -> Result<(), CryptoStoreError> {
308        let event = event.into();
309
310        let Ok(flow_id) = FlowId::try_from(&event) else {
311            // This isn't a verification event, return early.
312            return Ok(());
313        };
314        Span::current().record("flow_id", flow_id.as_str());
315
316        let flow_id_mismatch = || {
317            warn!(
318                flow_id = flow_id.as_str(),
319                "Received a verification event with a mismatched flow id, \
320                 the verification object was created for a in-room \
321                 verification but an event was received over to-device \
322                 messaging or vice versa"
323            );
324        };
325
326        let event_sent_from_us = |event: &AnyEvent<'_>, from_device: &DeviceId| {
327            if event.sender() == self.store.account.user_id {
328                from_device == self.store.account.device_id || event.is_room_event()
329            } else {
330                false
331            }
332        };
333
334        let Some(content) = event.verification_content() else { return Ok(()) };
335        match &content {
336            AnyVerificationContent::Request(r) => {
337                info!(
338                    sender = ?event.sender(),
339                    from_device = r.from_device().as_str(),
340                    "Received a new verification request",
341                );
342
343                let Some(timestamp) = event.timestamp() else {
344                    warn!(
345                        from_device = r.from_device().as_str(),
346                        "The key verification request didn't contain a valid timestamp"
347                    );
348                    return Ok(());
349                };
350
351                if !Self::is_timestamp_valid(timestamp) {
352                    info!(
353                        from_device = r.from_device().as_str(),
354                        ?timestamp,
355                        "The received verification request was too old or too far into the future",
356                    );
357                    return Ok(());
358                }
359
360                if event_sent_from_us(&event, r.from_device()) {
361                    trace!(
362                        from_device = r.from_device().as_str(),
363                        "The received verification request was sent by us, ignoring it",
364                    );
365                    return Ok(());
366                }
367
368                let Some(device_data) =
369                    self.store.get_device(event.sender(), r.from_device()).await?
370                else {
371                    warn!(
372                        "Could not retrieve the device data for the incoming verification request, \
373                         ignoring it"
374                    );
375                    return Ok(());
376                };
377
378                let request = VerificationRequest::from_request(
379                    self.verifications.clone(),
380                    self.store.clone(),
381                    event.sender(),
382                    flow_id,
383                    r,
384                    device_data,
385                );
386
387                self.insert_request(request);
388            }
389            AnyVerificationContent::Cancel(c) => {
390                if let Some(verification) = self.get_request(event.sender(), flow_id.as_str()) {
391                    verification.receive_cancel(event.sender(), c);
392                }
393
394                if let Some(verification) = self.get_verification(event.sender(), flow_id.as_str())
395                {
396                    match verification {
397                        Verification::SasV1(sas) => {
398                            // This won't produce an outgoing content
399                            let _ = sas.receive_any_event(event.sender(), &content);
400                        }
401                        #[cfg(feature = "qrcode")]
402                        Verification::QrV1(qr) => qr.receive_cancel(event.sender(), c),
403                    }
404                }
405            }
406            AnyVerificationContent::Ready(c) => {
407                let Some(request) = self.get_request(event.sender(), flow_id.as_str()) else {
408                    return Ok(());
409                };
410
411                if request.flow_id() == &flow_id {
412                    if let Some(device_data) =
413                        self.store.get_device(event.sender(), c.from_device()).await?
414                    {
415                        request.receive_ready(event.sender(), c, device_data);
416                    } else {
417                        warn!("Could not retrieve the data for the accepting device, ignoring it");
418                    }
419                } else {
420                    flow_id_mismatch();
421                }
422            }
423            AnyVerificationContent::Start(c) => {
424                if let Some(request) = self.get_request(event.sender(), flow_id.as_str()) {
425                    if request.flow_id() == &flow_id {
426                        Box::pin(request.receive_start(event.sender(), c)).await?
427                    } else {
428                        flow_id_mismatch();
429                    }
430                } else if let FlowId::ToDevice(_) = flow_id {
431                    // TODO remove this soon, this has been deprecated by
432                    // MSC3122 https://github.com/matrix-org/matrix-doc/pull/3122
433                    if let Some(device) =
434                        self.store.get_device(event.sender(), c.from_device()).await?
435                    {
436                        let identities = self.store.get_identities(device).await?;
437
438                        match Sas::from_start_event(flow_id, c, identities, None, false) {
439                            Ok(sas) => {
440                                self.verifications.insert_sas(sas);
441                            }
442                            Err(cancellation) => self.queue_up_content(
443                                event.sender(),
444                                c.from_device(),
445                                cancellation,
446                                None,
447                            ),
448                        }
449                    }
450                }
451            }
452            AnyVerificationContent::Accept(_) | AnyVerificationContent::Key(_) => {
453                let Some(sas) = self.get_sas(event.sender(), flow_id.as_str()) else {
454                    return Ok(());
455                };
456
457                if sas.flow_id() != &flow_id {
458                    flow_id_mismatch();
459                    return Ok(());
460                }
461
462                let Some((content, request_info)) = sas.receive_any_event(event.sender(), &content)
463                else {
464                    return Ok(());
465                };
466
467                self.queue_up_content(
468                    sas.other_user_id(),
469                    sas.other_device_id(),
470                    content,
471                    request_info,
472                );
473            }
474            AnyVerificationContent::Mac(_) => {
475                let Some(s) = self.get_sas(event.sender(), flow_id.as_str()) else { return Ok(()) };
476
477                if s.flow_id() != &flow_id {
478                    flow_id_mismatch();
479                    return Ok(());
480                }
481
482                let content = s.receive_any_event(event.sender(), &content);
483
484                if s.is_done() {
485                    Box::pin(self.mark_sas_as_done(&s, content.map(|(c, _)| c))).await?;
486                } else {
487                    // Even if we are not done (yet), there might be content to
488                    // send out, e.g. in the case where we are done with our
489                    // side of the verification process, but the other side has
490                    // not yet sent their "done".
491                    let Some((content, request_id)) = content else { return Ok(()) };
492
493                    self.queue_up_content(
494                        s.other_user_id(),
495                        s.other_device_id(),
496                        content,
497                        request_id,
498                    );
499                }
500            }
501            AnyVerificationContent::Done(c) => {
502                if let Some(verification) = self.get_request(event.sender(), flow_id.as_str()) {
503                    verification.receive_done(event.sender(), c);
504                }
505
506                #[allow(clippy::single_match)]
507                match self.get_verification(event.sender(), flow_id.as_str()) {
508                    Some(Verification::SasV1(sas)) => {
509                        let content = sas.receive_any_event(event.sender(), &content);
510
511                        if sas.is_done() {
512                            Box::pin(self.mark_sas_as_done(&sas, content.map(|(c, _)| c))).await?;
513                        }
514                    }
515                    #[cfg(feature = "qrcode")]
516                    Some(Verification::QrV1(qr)) => {
517                        let (cancellation, request) = Box::pin(qr.receive_done(c)).await?;
518
519                        if let Some(c) = cancellation {
520                            self.verifications.add_request(c.into())
521                        }
522
523                        if let Some(s) = request {
524                            self.verifications.add_request(s.into())
525                        }
526                    }
527                    None => {}
528                }
529            }
530        }
531
532        Ok(())
533    }
534
535    /**
536     * Utility function to build the public identity (i.e., an
537     * [`OwnUserIdentityData`]) corresponding to the private identity
538     * stored in the `VerificationStore`.
539     */
540    #[cfg(test)]
541    pub async fn get_own_user_identity_data(
542        &self,
543    ) -> Result<crate::OwnUserIdentityData, crate::SignatureError> {
544        self.store.private_identity.lock().await.to_public_identity().await
545    }
546}
547
548#[cfg(test)]
549mod tests {
550    use std::sync::Arc;
551
552    use matrix_sdk_test::async_test;
553    use ruma::TransactionId;
554    use tokio::sync::Mutex;
555
556    use super::{Sas, VerificationMachine};
557    use crate::{
558        Account, VerificationRequest,
559        olm::PrivateCrossSigningIdentity,
560        store::{CryptoStoreWrapper, MemoryStore},
561        verification::{
562            FlowId, VerificationStore,
563            cache::VerificationCache,
564            event_enums::{AcceptContent, KeyContent, MacContent, OutgoingContent},
565            tests::{alice_device_id, alice_id, setup_stores, wrap_any_to_device_content},
566        },
567    };
568
569    async fn verification_machine() -> (VerificationMachine, VerificationStore) {
570        let (_account, store, _bob, bob_store) = setup_stores().await;
571
572        let machine = VerificationMachine {
573            store,
574            verifications: VerificationCache::new(),
575            requests: Default::default(),
576        };
577
578        (machine, bob_store)
579    }
580
581    async fn setup_verification_machine() -> (VerificationMachine, Sas) {
582        let (machine, bob_store) = verification_machine().await;
583
584        let alice_device =
585            bob_store.get_device(alice_id(), alice_device_id()).await.unwrap().unwrap();
586
587        let identities = bob_store.get_identities(alice_device).await.unwrap();
588        let (bob_sas, start_content) =
589            Sas::start(identities, TransactionId::new(), true, None, None);
590
591        machine
592            .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content))
593            .await
594            .unwrap();
595
596        (machine, bob_sas)
597    }
598
599    #[async_test]
600    async fn test_create() {
601        let alice = Account::with_device_id(alice_id(), alice_device_id());
602        let identity = Arc::new(Mutex::new(PrivateCrossSigningIdentity::empty(alice_id())));
603        let _ = VerificationMachine::new(
604            alice.static_data,
605            identity,
606            Arc::new(CryptoStoreWrapper::new(alice_id(), alice_device_id(), MemoryStore::new())),
607        );
608    }
609
610    #[async_test]
611    async fn test_full_flow() {
612        let (alice_machine, bob) = setup_verification_machine().await;
613
614        let alice = alice_machine.get_sas(bob.user_id(), bob.flow_id().as_str()).unwrap();
615
616        let request = alice.accept().unwrap();
617
618        let content = OutgoingContent::try_from(request).unwrap();
619        let content = AcceptContent::try_from(&content).unwrap().into();
620
621        let (content, request_info) = bob.receive_any_event(alice.user_id(), &content).unwrap();
622
623        let event = wrap_any_to_device_content(bob.user_id(), content);
624
625        assert!(alice_machine.verifications.outgoing_requests().is_empty());
626        alice_machine.receive_any_event(&event).await.unwrap();
627        assert!(!alice_machine.verifications.outgoing_requests().is_empty());
628
629        let request = alice_machine.verifications.outgoing_requests().first().cloned().unwrap();
630        let txn_id = request.request_id().to_owned();
631        let content = OutgoingContent::try_from(request).unwrap();
632        let content = KeyContent::try_from(&content).unwrap().into();
633
634        alice_machine.mark_request_as_sent(&txn_id);
635
636        assert!(bob.receive_any_event(alice.user_id(), &content).is_none());
637
638        assert!(alice.emoji().is_some());
639        // Bob can only show the emoji if it marks the request carrying the
640        // m.key.verification.key event as sent.
641        assert!(bob.emoji().is_none());
642        bob.mark_request_as_sent(&request_info.unwrap().request_id);
643        assert!(bob.emoji().is_some());
644        assert_eq!(alice.emoji(), bob.emoji());
645
646        let mut requests = alice.confirm().await.unwrap().0;
647        assert!(requests.len() == 1);
648        let request = requests.pop().unwrap();
649        let content = OutgoingContent::try_from(request).unwrap();
650        let content = MacContent::try_from(&content).unwrap().into();
651        bob.receive_any_event(alice.user_id(), &content);
652
653        let mut requests = bob.confirm().await.unwrap().0;
654        assert!(requests.len() == 1);
655        let request = requests.pop().unwrap();
656        let content = OutgoingContent::try_from(request).unwrap();
657        let content = MacContent::try_from(&content).unwrap().into();
658        alice.receive_any_event(bob.user_id(), &content);
659
660        assert!(alice.is_done());
661        assert!(bob.is_done());
662    }
663
664    #[cfg(not(target_os = "macos"))]
665    #[allow(unknown_lints, clippy::unchecked_duration_subtraction)]
666    #[async_test]
667    async fn test_timing_out() {
668        use std::time::Duration;
669
670        use ruma::time::Instant;
671
672        let (alice_machine, bob) = setup_verification_machine().await;
673        let alice = alice_machine.get_sas(bob.user_id(), bob.flow_id().as_str()).unwrap();
674
675        assert!(!alice.timed_out());
676        assert!(alice_machine.verifications.outgoing_requests().is_empty());
677
678        // This line panics on macOS, so we're disabled for now.
679        alice.set_creation_time(Instant::now() - Duration::from_secs(60 * 15));
680        assert!(alice.timed_out());
681        assert!(alice_machine.verifications.outgoing_requests().is_empty());
682        alice_machine.garbage_collect();
683        assert!(!alice_machine.verifications.outgoing_requests().is_empty());
684        alice_machine.garbage_collect();
685        assert!(alice_machine.verifications.is_empty());
686    }
687
688    /// Test to ensure that we cancel both verifications if a second one gets
689    /// started while another one is going on.
690    #[async_test]
691    async fn test_double_verification_cancellation() {
692        let (machine, bob_store) = verification_machine().await;
693
694        let alice_device =
695            bob_store.get_device(alice_id(), alice_device_id()).await.unwrap().unwrap();
696        let identities = bob_store.get_identities(alice_device).await.unwrap();
697
698        // Start the first sas verification.
699        let (bob_sas, start_content) =
700            Sas::start(identities.clone(), TransactionId::new(), true, None, None);
701
702        machine
703            .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content))
704            .await
705            .unwrap();
706
707        let alice_sas = machine.get_sas(bob_sas.user_id(), bob_sas.flow_id().as_str()).unwrap();
708
709        // We're not yet cancelled.
710        assert!(!alice_sas.is_cancelled());
711
712        let second_transaction_id = TransactionId::new();
713        let (bob_sas, start_content) =
714            Sas::start(identities, second_transaction_id.clone(), true, None, None);
715        machine
716            .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content))
717            .await
718            .unwrap();
719
720        let second_sas = machine.get_sas(bob_sas.user_id(), bob_sas.flow_id().as_str()).unwrap();
721
722        // Make sure we fetched the new one.
723        assert_eq!(second_sas.flow_id().as_str(), second_transaction_id);
724
725        // Make sure both of them are cancelled.
726        assert!(alice_sas.is_cancelled());
727        assert!(second_sas.is_cancelled());
728    }
729
730    /// Test to ensure that we cancel both verification requests if a second one
731    /// gets started while another one is going on.
732    #[async_test]
733    async fn test_double_verification_request_cancellation() {
734        let (machine, bob_store) = verification_machine().await;
735
736        // Start the first verification request.
737        let flow_id = FlowId::ToDevice("TEST_FLOW_ID".into());
738
739        let bob_request = VerificationRequest::new(
740            VerificationCache::new(),
741            bob_store.clone(),
742            flow_id.clone(),
743            alice_id(),
744            vec![],
745            None,
746        );
747
748        let request = bob_request.request_to_device();
749        let content: OutgoingContent = request.try_into().unwrap();
750
751        machine
752            .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
753            .await
754            .unwrap();
755
756        let alice_request =
757            machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
758
759        // We're not yet cancelled.
760        assert!(!alice_request.is_cancelled());
761
762        let second_transaction_id = TransactionId::new();
763        let bob_request = VerificationRequest::new(
764            VerificationCache::new(),
765            bob_store,
766            second_transaction_id.clone().into(),
767            alice_id(),
768            vec![],
769            None,
770        );
771
772        let request = bob_request.request_to_device();
773        let content: OutgoingContent = request.try_into().unwrap();
774
775        machine
776            .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
777            .await
778            .unwrap();
779
780        let second_request =
781            machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
782
783        // Make sure we fetched the new one.
784        assert_eq!(second_request.flow_id().as_str(), second_transaction_id);
785
786        // Make sure both of them are cancelled.
787        assert!(alice_request.is_cancelled());
788        assert!(second_request.is_cancelled());
789    }
790
791    /// Ensure that if a duplicate request is added (i.e. matching user and
792    /// flow_id) the existing request is not cancelled and the new one is
793    /// ignored
794    #[async_test]
795    async fn test_ignore_identical_verification_request() {
796        let (machine, bob_store) = verification_machine().await;
797
798        // Start the first verification request.
799        let flow_id = FlowId::ToDevice("TEST_FLOW_ID".into());
800
801        let bob_request = VerificationRequest::new(
802            VerificationCache::new(),
803            bob_store.clone(),
804            flow_id.clone(),
805            alice_id(),
806            vec![],
807            None,
808        );
809
810        let request = bob_request.request_to_device();
811        let content: OutgoingContent = request.try_into().unwrap();
812
813        machine
814            .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
815            .await
816            .unwrap();
817
818        let first_request =
819            machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
820
821        // We're not yet cancelled.
822        assert!(!first_request.is_cancelled());
823
824        // Bob is adding a second request with the same flow_id as before
825        let bob_request = VerificationRequest::new(
826            VerificationCache::new(),
827            bob_store,
828            flow_id.clone(),
829            alice_id(),
830            vec![],
831            None,
832        );
833
834        let request = bob_request.request_to_device();
835        let content: OutgoingContent = request.try_into().unwrap();
836
837        machine
838            .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
839            .await
840            .unwrap();
841
842        let second_request =
843            machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
844
845        // None of the requests are cancelled
846        assert!(!first_request.is_cancelled());
847        assert!(!second_request.is_cancelled());
848    }
849}