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.read().get(user_id).map(|v| v.values().cloned().collect()).unwrap_or_default()
159 }
160
161 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 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 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 let old_timestamp_threshold: UInt = uint!(600);
210 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 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 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 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 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 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 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 #[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 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 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 assert_eq!(second_sas.flow_id().as_str(), second_transaction_id);
711
712 assert!(alice_sas.is_cancelled());
714 assert!(second_sas.is_cancelled());
715 }
716
717 #[async_test]
720 async fn test_double_verification_request_cancellation() {
721 let (machine, bob_store) = verification_machine().await;
722
723 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 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 assert_eq!(second_request.flow_id().as_str(), second_transaction_id);
772
773 assert!(alice_request.is_cancelled());
775 assert!(second_request.is_cancelled());
776 }
777
778 #[async_test]
782 async fn test_ignore_identical_verification_request() {
783 let (machine, bob_store) = verification_machine().await;
784
785 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 assert!(!first_request.is_cancelled());
810
811 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 assert!(!first_request.is_cancelled());
834 assert!(!second_request.is_cancelled());
835 }
836}