1use 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
159 .read()
160 .get(user_id)
161 .map(|v| v.iter().map(|(_, value)| value.clone()).collect())
162 .unwrap_or_default()
163 }
164
165 fn insert_request(&self, request: VerificationRequest) {
169 if let Some(r) = self.get_request(request.other_user(), request.flow_id().as_str()) {
170 debug!(flow_id = r.flow_id().as_str(), "Ignoring known verification request",);
171 return;
172 }
173
174 let mut requests = self.requests.write();
175 let user_requests = requests.entry(request.other_user().to_owned()).or_default();
176
177 for old_verification in user_requests.values_mut() {
181 if !old_verification.is_cancelled() {
182 warn!(
183 "Received a new verification request whilst another request \
184 with the same user is ongoing. Cancelling both requests."
185 );
186
187 if let Some(r) = old_verification.cancel() {
188 self.verifications.add_request(r.into())
189 }
190
191 if let Some(r) = request.cancel() {
192 self.verifications.add_request(r.into())
193 }
194 }
195 }
196
197 user_requests.insert(request.flow_id().as_str().to_owned(), request);
201 }
202
203 pub fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option<Verification> {
204 self.verifications.get(user_id, flow_id)
205 }
206
207 pub fn get_sas(&self, user_id: &UserId, flow_id: &str) -> Option<Sas> {
208 self.verifications.get_sas(user_id, flow_id)
209 }
210
211 fn is_timestamp_valid(timestamp: MilliSecondsSinceUnixEpoch) -> bool {
212 let old_timestamp_threshold: UInt = uint!(600);
214 let timestamp_threshold: UInt = uint!(300);
217
218 let timestamp = timestamp.as_secs();
219 let now = SecondsSinceUnixEpoch::now().get();
220
221 !(now.saturating_sub(timestamp) > old_timestamp_threshold
222 || timestamp.saturating_sub(now) > timestamp_threshold)
223 }
224
225 fn queue_up_content(
226 &self,
227 recipient: &UserId,
228 recipient_device: &DeviceId,
229 content: OutgoingContent,
230 request_id: Option<RequestInfo>,
231 ) {
232 self.verifications.queue_up_content(recipient, recipient_device, content, request_id)
233 }
234
235 pub fn mark_request_as_sent(&self, request_id: &TransactionId) {
236 self.verifications.mark_request_as_sent(request_id);
237 }
238
239 pub fn outgoing_messages(&self) -> Vec<OutgoingRequest> {
240 self.verifications.outgoing_requests()
241 }
242
243 pub fn garbage_collect(&self) -> Vec<Raw<AnyToDeviceEvent>> {
244 let mut events = vec![];
245
246 let mut requests: Vec<OutgoingVerificationRequest> = {
247 let mut requests = self.requests.write();
248
249 for user_verification in requests.values_mut() {
250 user_verification.retain(|_, v| !(v.is_done() || v.is_cancelled()));
251 }
252 requests.retain(|_, v| !v.is_empty());
253
254 requests.values().flatten().filter_map(|(_, v)| v.cancel_if_timed_out()).collect()
255 };
256
257 requests.extend(self.verifications.garbage_collect());
258
259 for request in requests {
260 if let Ok(OutgoingContent::ToDevice(AnyToDeviceEventContent::KeyVerificationCancel(
261 content,
262 ))) = request.clone().try_into()
263 {
264 let event = ToDeviceEvent { content, sender: self.own_user_id().to_owned() };
265
266 events.push(
267 Raw::new(&event)
268 .expect("Failed to serialize m.key_verification.cancel event")
269 .cast(),
270 );
271 }
272
273 self.verifications.add_verification_request(request)
274 }
275
276 events
277 }
278
279 async fn mark_sas_as_done(
280 &self,
281 sas: &Sas,
282 out_content: Option<OutgoingContent>,
283 ) -> Result<(), CryptoStoreError> {
284 match sas.mark_as_done().await? {
285 VerificationResult::Ok => {
286 if let Some(c) = out_content {
287 self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c, None);
288 }
289 }
290 VerificationResult::Cancel(c) => {
291 if let Some(r) = sas.cancel_with_code(c) {
292 self.verifications.add_request(r.into());
293 }
294 }
295 VerificationResult::SignatureUpload(r) => {
296 self.verifications.add_request(r.into());
297
298 if let Some(c) = out_content {
299 self.queue_up_content(sas.other_user_id(), sas.other_device_id(), c, None);
300 }
301 }
302 }
303
304 Ok(())
305 }
306
307 #[instrument(skip_all, fields(flow_id))]
308 pub async fn receive_any_event(
309 &self,
310 event: impl Into<AnyEvent<'_>>,
311 ) -> Result<(), CryptoStoreError> {
312 let event = event.into();
313
314 let Ok(flow_id) = FlowId::try_from(&event) else {
315 return Ok(());
317 };
318 Span::current().record("flow_id", flow_id.as_str());
319
320 let flow_id_mismatch = || {
321 warn!(
322 flow_id = flow_id.as_str(),
323 "Received a verification event with a mismatched flow id, \
324 the verification object was created for a in-room \
325 verification but an event was received over to-device \
326 messaging or vice versa"
327 );
328 };
329
330 let event_sent_from_us = |event: &AnyEvent<'_>, from_device: &DeviceId| {
331 if event.sender() == self.store.account.user_id {
332 from_device == self.store.account.device_id || event.is_room_event()
333 } else {
334 false
335 }
336 };
337
338 let Some(content) = event.verification_content() else { return Ok(()) };
339 match &content {
340 AnyVerificationContent::Request(r) => {
341 info!(
342 sender = ?event.sender(),
343 from_device = r.from_device().as_str(),
344 "Received a new verification request",
345 );
346
347 let Some(timestamp) = event.timestamp() else {
348 warn!(
349 from_device = r.from_device().as_str(),
350 "The key verification request didn't contain a valid timestamp"
351 );
352 return Ok(());
353 };
354
355 if !Self::is_timestamp_valid(timestamp) {
356 trace!(
357 from_device = r.from_device().as_str(),
358 ?timestamp,
359 "The received verification request was too old or too far into the future",
360 );
361 return Ok(());
362 }
363
364 if event_sent_from_us(&event, r.from_device()) {
365 trace!(
366 from_device = r.from_device().as_str(),
367 "The received verification request was sent by us, ignoring it",
368 );
369 return Ok(());
370 }
371
372 let Some(device_data) =
373 self.store.get_device(event.sender(), r.from_device()).await?
374 else {
375 warn!("Could not retrieve the device data for the incoming verification request, ignoring it");
376 return Ok(());
377 };
378
379 let request = VerificationRequest::from_request(
380 self.verifications.clone(),
381 self.store.clone(),
382 event.sender(),
383 flow_id,
384 r,
385 device_data,
386 );
387
388 self.insert_request(request);
389 }
390 AnyVerificationContent::Cancel(c) => {
391 if let Some(verification) = self.get_request(event.sender(), flow_id.as_str()) {
392 verification.receive_cancel(event.sender(), c);
393 }
394
395 if let Some(verification) = self.get_verification(event.sender(), flow_id.as_str())
396 {
397 match verification {
398 Verification::SasV1(sas) => {
399 let _ = sas.receive_any_event(event.sender(), &content);
401 }
402 #[cfg(feature = "qrcode")]
403 Verification::QrV1(qr) => qr.receive_cancel(event.sender(), c),
404 }
405 }
406 }
407 AnyVerificationContent::Ready(c) => {
408 let Some(request) = self.get_request(event.sender(), flow_id.as_str()) else {
409 return Ok(());
410 };
411
412 if request.flow_id() == &flow_id {
413 if let Some(device_data) =
414 self.store.get_device(event.sender(), c.from_device()).await?
415 {
416 request.receive_ready(event.sender(), c, device_data);
417 } else {
418 warn!("Could not retrieve the data for the accepting device, ignoring it");
419 }
420 } else {
421 flow_id_mismatch();
422 }
423 }
424 AnyVerificationContent::Start(c) => {
425 if let Some(request) = self.get_request(event.sender(), flow_id.as_str()) {
426 if request.flow_id() == &flow_id {
427 Box::pin(request.receive_start(event.sender(), c)).await?
428 } else {
429 flow_id_mismatch();
430 }
431 } else if let FlowId::ToDevice(_) = flow_id {
432 if let Some(device) =
435 self.store.get_device(event.sender(), c.from_device()).await?
436 {
437 let identities = self.store.get_identities(device).await?;
438
439 match Sas::from_start_event(flow_id, c, identities, None, false) {
440 Ok(sas) => {
441 self.verifications.insert_sas(sas);
442 }
443 Err(cancellation) => self.queue_up_content(
444 event.sender(),
445 c.from_device(),
446 cancellation,
447 None,
448 ),
449 }
450 }
451 }
452 }
453 AnyVerificationContent::Accept(_) | AnyVerificationContent::Key(_) => {
454 let Some(sas) = self.get_sas(event.sender(), flow_id.as_str()) else {
455 return Ok(());
456 };
457
458 if sas.flow_id() != &flow_id {
459 flow_id_mismatch();
460 return Ok(());
461 }
462
463 let Some((content, request_info)) = sas.receive_any_event(event.sender(), &content)
464 else {
465 return Ok(());
466 };
467
468 self.queue_up_content(
469 sas.other_user_id(),
470 sas.other_device_id(),
471 content,
472 request_info,
473 );
474 }
475 AnyVerificationContent::Mac(_) => {
476 let Some(s) = self.get_sas(event.sender(), flow_id.as_str()) else { return Ok(()) };
477
478 if s.flow_id() != &flow_id {
479 flow_id_mismatch();
480 return Ok(());
481 }
482
483 let content = s.receive_any_event(event.sender(), &content);
484
485 if s.is_done() {
486 Box::pin(self.mark_sas_as_done(&s, content.map(|(c, _)| c))).await?;
487 } else {
488 let Some((content, request_id)) = content else { return Ok(()) };
493
494 self.queue_up_content(
495 s.other_user_id(),
496 s.other_device_id(),
497 content,
498 request_id,
499 );
500 }
501 }
502 AnyVerificationContent::Done(c) => {
503 if let Some(verification) = self.get_request(event.sender(), flow_id.as_str()) {
504 verification.receive_done(event.sender(), c);
505 }
506
507 #[allow(clippy::single_match)]
508 match self.get_verification(event.sender(), flow_id.as_str()) {
509 Some(Verification::SasV1(sas)) => {
510 let content = sas.receive_any_event(event.sender(), &content);
511
512 if sas.is_done() {
513 Box::pin(self.mark_sas_as_done(&sas, content.map(|(c, _)| c))).await?;
514 }
515 }
516 #[cfg(feature = "qrcode")]
517 Some(Verification::QrV1(qr)) => {
518 let (cancellation, request) = Box::pin(qr.receive_done(c)).await?;
519
520 if let Some(c) = cancellation {
521 self.verifications.add_request(c.into())
522 }
523
524 if let Some(s) = request {
525 self.verifications.add_request(s.into())
526 }
527 }
528 None => {}
529 }
530 }
531 }
532
533 Ok(())
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use std::sync::Arc;
540
541 use matrix_sdk_test::async_test;
542 use ruma::TransactionId;
543 use tokio::sync::Mutex;
544
545 use super::{Sas, VerificationMachine};
546 use crate::{
547 olm::PrivateCrossSigningIdentity,
548 store::{CryptoStoreWrapper, MemoryStore},
549 verification::{
550 cache::VerificationCache,
551 event_enums::{AcceptContent, KeyContent, MacContent, OutgoingContent},
552 tests::{alice_device_id, alice_id, setup_stores, wrap_any_to_device_content},
553 FlowId, VerificationStore,
554 },
555 Account, VerificationRequest,
556 };
557
558 async fn verification_machine() -> (VerificationMachine, VerificationStore) {
559 let (_account, store, _bob, bob_store) = setup_stores().await;
560
561 let machine = VerificationMachine {
562 store,
563 verifications: VerificationCache::new(),
564 requests: Default::default(),
565 };
566
567 (machine, bob_store)
568 }
569
570 async fn setup_verification_machine() -> (VerificationMachine, Sas) {
571 let (machine, bob_store) = verification_machine().await;
572
573 let alice_device =
574 bob_store.get_device(alice_id(), alice_device_id()).await.unwrap().unwrap();
575
576 let identities = bob_store.get_identities(alice_device).await.unwrap();
577 let (bob_sas, start_content) =
578 Sas::start(identities, TransactionId::new(), true, None, None);
579
580 machine
581 .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content))
582 .await
583 .unwrap();
584
585 (machine, bob_sas)
586 }
587
588 #[async_test]
589 async fn test_create() {
590 let alice = Account::with_device_id(alice_id(), alice_device_id());
591 let identity = Arc::new(Mutex::new(PrivateCrossSigningIdentity::empty(alice_id())));
592 let _ = VerificationMachine::new(
593 alice.static_data,
594 identity,
595 Arc::new(CryptoStoreWrapper::new(alice_id(), alice_device_id(), MemoryStore::new())),
596 );
597 }
598
599 #[async_test]
600 async fn test_full_flow() {
601 let (alice_machine, bob) = setup_verification_machine().await;
602
603 let alice = alice_machine.get_sas(bob.user_id(), bob.flow_id().as_str()).unwrap();
604
605 let request = alice.accept().unwrap();
606
607 let content = OutgoingContent::try_from(request).unwrap();
608 let content = AcceptContent::try_from(&content).unwrap().into();
609
610 let (content, request_info) = bob.receive_any_event(alice.user_id(), &content).unwrap();
611
612 let event = wrap_any_to_device_content(bob.user_id(), content);
613
614 assert!(alice_machine.verifications.outgoing_requests().is_empty());
615 alice_machine.receive_any_event(&event).await.unwrap();
616 assert!(!alice_machine.verifications.outgoing_requests().is_empty());
617
618 let request = alice_machine.verifications.outgoing_requests().first().cloned().unwrap();
619 let txn_id = request.request_id().to_owned();
620 let content = OutgoingContent::try_from(request).unwrap();
621 let content = KeyContent::try_from(&content).unwrap().into();
622
623 alice_machine.mark_request_as_sent(&txn_id);
624
625 assert!(bob.receive_any_event(alice.user_id(), &content).is_none());
626
627 assert!(alice.emoji().is_some());
628 assert!(bob.emoji().is_none());
631 bob.mark_request_as_sent(&request_info.unwrap().request_id);
632 assert!(bob.emoji().is_some());
633 assert_eq!(alice.emoji(), bob.emoji());
634
635 let mut requests = alice.confirm().await.unwrap().0;
636 assert!(requests.len() == 1);
637 let request = requests.pop().unwrap();
638 let content = OutgoingContent::try_from(request).unwrap();
639 let content = MacContent::try_from(&content).unwrap().into();
640 bob.receive_any_event(alice.user_id(), &content);
641
642 let mut requests = bob.confirm().await.unwrap().0;
643 assert!(requests.len() == 1);
644 let request = requests.pop().unwrap();
645 let content = OutgoingContent::try_from(request).unwrap();
646 let content = MacContent::try_from(&content).unwrap().into();
647 alice.receive_any_event(bob.user_id(), &content);
648
649 assert!(alice.is_done());
650 assert!(bob.is_done());
651 }
652
653 #[cfg(not(target_os = "macos"))]
654 #[allow(unknown_lints, clippy::unchecked_duration_subtraction)]
655 #[async_test]
656 async fn test_timing_out() {
657 use std::time::Duration;
658
659 use ruma::time::Instant;
660
661 let (alice_machine, bob) = setup_verification_machine().await;
662 let alice = alice_machine.get_sas(bob.user_id(), bob.flow_id().as_str()).unwrap();
663
664 assert!(!alice.timed_out());
665 assert!(alice_machine.verifications.outgoing_requests().is_empty());
666
667 alice.set_creation_time(Instant::now() - Duration::from_secs(60 * 15));
669 assert!(alice.timed_out());
670 assert!(alice_machine.verifications.outgoing_requests().is_empty());
671 alice_machine.garbage_collect();
672 assert!(!alice_machine.verifications.outgoing_requests().is_empty());
673 alice_machine.garbage_collect();
674 assert!(alice_machine.verifications.is_empty());
675 }
676
677 #[async_test]
680 async fn test_double_verification_cancellation() {
681 let (machine, bob_store) = verification_machine().await;
682
683 let alice_device =
684 bob_store.get_device(alice_id(), alice_device_id()).await.unwrap().unwrap();
685 let identities = bob_store.get_identities(alice_device).await.unwrap();
686
687 let (bob_sas, start_content) =
689 Sas::start(identities.clone(), TransactionId::new(), true, None, None);
690
691 machine
692 .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content))
693 .await
694 .unwrap();
695
696 let alice_sas = machine.get_sas(bob_sas.user_id(), bob_sas.flow_id().as_str()).unwrap();
697
698 assert!(!alice_sas.is_cancelled());
700
701 let second_transaction_id = TransactionId::new();
702 let (bob_sas, start_content) =
703 Sas::start(identities, second_transaction_id.clone(), true, None, None);
704 machine
705 .receive_any_event(&wrap_any_to_device_content(bob_sas.user_id(), start_content))
706 .await
707 .unwrap();
708
709 let second_sas = machine.get_sas(bob_sas.user_id(), bob_sas.flow_id().as_str()).unwrap();
710
711 assert_eq!(second_sas.flow_id().as_str(), second_transaction_id);
713
714 assert!(alice_sas.is_cancelled());
716 assert!(second_sas.is_cancelled());
717 }
718
719 #[async_test]
722 async fn test_double_verification_request_cancellation() {
723 let (machine, bob_store) = verification_machine().await;
724
725 let flow_id = FlowId::ToDevice("TEST_FLOW_ID".into());
727
728 let bob_request = VerificationRequest::new(
729 VerificationCache::new(),
730 bob_store.clone(),
731 flow_id.clone(),
732 alice_id(),
733 vec![],
734 None,
735 );
736
737 let request = bob_request.request_to_device();
738 let content: OutgoingContent = request.try_into().unwrap();
739
740 machine
741 .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
742 .await
743 .unwrap();
744
745 let alice_request =
746 machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
747
748 assert!(!alice_request.is_cancelled());
750
751 let second_transaction_id = TransactionId::new();
752 let bob_request = VerificationRequest::new(
753 VerificationCache::new(),
754 bob_store,
755 second_transaction_id.clone().into(),
756 alice_id(),
757 vec![],
758 None,
759 );
760
761 let request = bob_request.request_to_device();
762 let content: OutgoingContent = request.try_into().unwrap();
763
764 machine
765 .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
766 .await
767 .unwrap();
768
769 let second_request =
770 machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
771
772 assert_eq!(second_request.flow_id().as_str(), second_transaction_id);
774
775 assert!(alice_request.is_cancelled());
777 assert!(second_request.is_cancelled());
778 }
779
780 #[async_test]
784 async fn test_ignore_identical_verification_request() {
785 let (machine, bob_store) = verification_machine().await;
786
787 let flow_id = FlowId::ToDevice("TEST_FLOW_ID".into());
789
790 let bob_request = VerificationRequest::new(
791 VerificationCache::new(),
792 bob_store.clone(),
793 flow_id.clone(),
794 alice_id(),
795 vec![],
796 None,
797 );
798
799 let request = bob_request.request_to_device();
800 let content: OutgoingContent = request.try_into().unwrap();
801
802 machine
803 .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
804 .await
805 .unwrap();
806
807 let first_request =
808 machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
809
810 assert!(!first_request.is_cancelled());
812
813 let bob_request = VerificationRequest::new(
815 VerificationCache::new(),
816 bob_store,
817 flow_id.clone(),
818 alice_id(),
819 vec![],
820 None,
821 );
822
823 let request = bob_request.request_to_device();
824 let content: OutgoingContent = request.try_into().unwrap();
825
826 machine
827 .receive_any_event(&wrap_any_to_device_content(bob_request.own_user_id(), content))
828 .await
829 .unwrap();
830
831 let second_request =
832 machine.get_request(bob_request.own_user_id(), bob_request.flow_id().as_str()).unwrap();
833
834 assert!(!first_request.is_cancelled());
836 assert!(!second_request.is_cancelled());
837 }
838}