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