1use std::{ops::Add, sync::Arc, time::Duration};
16
17use as_variant::as_variant;
18use eyeball::{ObservableWriteGuard, SharedObservable, WeakObservable};
19use futures_core::Stream;
20use futures_util::StreamExt;
21#[cfg(feature = "qrcode")]
22use matrix_sdk_qrcode::QrVerificationData;
23use ruma::{
24 DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, RoomId, TransactionId,
25 UserId,
26 events::{
27 AnyMessageLikeEventContent, AnyToDeviceEventContent,
28 key::verification::{
29 VerificationMethod,
30 cancel::CancelCode,
31 ready::{KeyVerificationReadyEventContent, ToDeviceKeyVerificationReadyEventContent},
32 request::ToDeviceKeyVerificationRequestEventContent,
33 start::StartMethod,
34 },
35 relation::Reference,
36 room::message::KeyVerificationRequestEventContent,
37 },
38 time::Instant,
39 to_device::DeviceIdOrAllDevices,
40};
41#[cfg(feature = "qrcode")]
42use tracing::debug;
43use tracing::{info, trace, warn};
44
45#[cfg(feature = "qrcode")]
46use super::qrcode::{QrVerification, QrVerificationState, ScanError};
47use super::{
48 CancelInfo, Cancelled, FlowId, Verification, VerificationStore,
49 cache::VerificationCache,
50 event_enums::{
51 CancelContent, DoneContent, OutgoingContent, ReadyContent, RequestContent, StartContent,
52 },
53};
54use crate::{
55 CryptoStoreError, DeviceData, Sas,
56 olm::StaticAccountData,
57 types::requests::{OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest},
58};
59
60const SUPPORTED_METHODS: &[VerificationMethod] = &[
61 VerificationMethod::SasV1,
62 #[cfg(feature = "qrcode")]
63 VerificationMethod::QrCodeShowV1,
64 VerificationMethod::ReciprocateV1,
65];
66
67const VERIFICATION_TIMEOUT: Duration = Duration::from_secs(60 * 10);
68
69#[derive(Debug, Clone)]
71pub enum VerificationRequestState {
72 Created {
74 our_methods: Vec<VerificationMethod>,
76 },
77 Requested {
79 their_methods: Vec<VerificationMethod>,
81
82 other_device_data: DeviceData,
85 },
86 Ready {
88 their_methods: Vec<VerificationMethod>,
90
91 our_methods: Vec<VerificationMethod>,
93
94 other_device_data: DeviceData,
97 },
98 Transitioned {
101 verification: Verification,
104
105 other_device_data: DeviceData,
108 },
109 Done,
111 Cancelled(CancelInfo),
113}
114
115impl From<&InnerRequest> for VerificationRequestState {
116 fn from(value: &InnerRequest) -> Self {
117 match value {
118 InnerRequest::Created(s) => {
119 Self::Created { our_methods: s.state.our_methods.to_owned() }
120 }
121 InnerRequest::Requested(s) => Self::Requested {
122 their_methods: s.state.their_methods.to_owned(),
123 other_device_data: s.state.other_device_data.to_owned(),
124 },
125 InnerRequest::Ready(s) => Self::Ready {
126 their_methods: s.state.their_methods.to_owned(),
127 our_methods: s.state.our_methods.to_owned(),
128 other_device_data: s.state.other_device_data.to_owned(),
129 },
130 InnerRequest::Transitioned(s) => Self::Transitioned {
131 verification: s.state.verification.to_owned(),
132 other_device_data: s.state.other_device_data.to_owned(),
133 },
134 InnerRequest::Passive(_) => {
135 Self::Cancelled(Cancelled::new(true, CancelCode::Accepted).into())
136 }
137 InnerRequest::Done(_) => Self::Done,
138 InnerRequest::Cancelled(s) => Self::Cancelled(s.state.to_owned().into()),
139 }
140 }
141}
142
143#[derive(Clone, Debug)]
151pub struct VerificationRequest {
152 verification_cache: VerificationCache,
153 account: StaticAccountData,
154 flow_id: Arc<FlowId>,
155 other_user_id: OwnedUserId,
156 inner: SharedObservable<InnerRequest>,
157 creation_time: Arc<Instant>,
158 we_started: bool,
159 recipient_devices: Arc<Vec<OwnedDeviceId>>,
160}
161
162#[derive(Debug, Clone)]
171pub(crate) struct RequestHandle {
172 inner: WeakObservable<InnerRequest>,
173}
174
175impl RequestHandle {
176 pub fn cancel_with_code(&self, cancel_code: &CancelCode) {
177 if let Some(observable) = self.inner.upgrade() {
178 let mut guard = observable.write();
179
180 if let Some(updated) = guard.cancel(true, cancel_code) {
181 ObservableWriteGuard::set(&mut guard, updated);
182 }
183 }
184 }
185}
186
187impl From<SharedObservable<InnerRequest>> for RequestHandle {
188 fn from(inner: SharedObservable<InnerRequest>) -> Self {
189 let inner = inner.downgrade();
190
191 Self { inner }
192 }
193}
194
195impl VerificationRequest {
196 pub(crate) fn new(
197 cache: VerificationCache,
198 store: VerificationStore,
199 flow_id: FlowId,
200 other_user: &UserId,
201 recipient_devices: Vec<OwnedDeviceId>,
202 methods: Option<Vec<VerificationMethod>>,
203 ) -> Self {
204 let account = store.account.clone();
205 let inner = SharedObservable::new(InnerRequest::Created(RequestState::new(
206 cache.clone(),
207 store,
208 other_user,
209 &flow_id,
210 methods,
211 )));
212
213 Self {
214 account,
215 verification_cache: cache,
216 flow_id: flow_id.into(),
217 inner,
218 other_user_id: other_user.into(),
219 creation_time: Instant::now().into(),
220 we_started: true,
221 recipient_devices: recipient_devices.into(),
222 }
223 }
224
225 pub(crate) fn request_to_device(&self) -> ToDeviceRequest {
230 let inner = self.inner.read();
231
232 let methods = if let InnerRequest::Created(c) = &*inner {
233 c.state.our_methods.clone()
234 } else {
235 SUPPORTED_METHODS.to_vec()
236 };
237
238 let content = ToDeviceKeyVerificationRequestEventContent::new(
239 self.account.device_id.clone(),
240 self.flow_id().as_str().into(),
241 methods,
242 MilliSecondsSinceUnixEpoch::now(),
243 );
244
245 ToDeviceRequest::for_recipients(
246 self.other_user(),
247 self.recipient_devices.to_vec(),
248 &AnyToDeviceEventContent::KeyVerificationRequest(content),
249 TransactionId::new(),
250 )
251 }
252
253 pub fn request(
258 own_user_id: &UserId,
259 own_device_id: &DeviceId,
260 other_user_id: &UserId,
261 methods: Option<Vec<VerificationMethod>>,
262 ) -> KeyVerificationRequestEventContent {
263 KeyVerificationRequestEventContent::new(
264 format!(
265 "{own_user_id} is requesting to verify your key, but your client does not \
266 support in-chat key verification. You will need to use legacy \
267 key verification to verify keys."
268 ),
269 methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec()),
270 own_device_id.into(),
271 other_user_id.to_owned(),
272 )
273 }
274
275 pub fn own_user_id(&self) -> &UserId {
277 &self.account.user_id
278 }
279
280 pub fn other_user(&self) -> &UserId {
283 &self.other_user_id
284 }
285
286 pub fn other_device_id(&self) -> Option<OwnedDeviceId> {
288 match &*self.inner.read() {
289 InnerRequest::Requested(r) => Some(r.state.other_device_data.device_id().to_owned()),
290 InnerRequest::Ready(r) => Some(r.state.other_device_data.device_id().to_owned()),
291 InnerRequest::Transitioned(r) => {
292 Some(r.state.ready.other_device_data.device_id().to_owned())
293 }
294 InnerRequest::Created(_)
295 | InnerRequest::Passive(_)
296 | InnerRequest::Done(_)
297 | InnerRequest::Cancelled(_) => None,
298 }
299 }
300
301 pub fn room_id(&self) -> Option<&RoomId> {
303 match self.flow_id.as_ref() {
304 FlowId::ToDevice(_) => None,
305 FlowId::InRoom(r, _) => Some(r),
306 }
307 }
308
309 pub fn cancel_info(&self) -> Option<CancelInfo> {
312 as_variant!(&*self.inner.read(), InnerRequest::Cancelled(c) => {
313 c.state.clone().into()
314 })
315 }
316
317 pub fn is_passive(&self) -> bool {
319 matches!(*self.inner.read(), InnerRequest::Passive(_))
320 }
321
322 pub fn is_ready(&self) -> bool {
324 matches!(*self.inner.read(), InnerRequest::Ready(_))
325 }
326
327 pub fn timed_out(&self) -> bool {
329 self.creation_time.elapsed() > VERIFICATION_TIMEOUT
330 }
331
332 pub fn time_remaining(&self) -> Duration {
335 self.creation_time
336 .add(VERIFICATION_TIMEOUT)
337 .checked_duration_since(Instant::now())
338 .unwrap_or(Duration::from_secs(0))
339 }
340
341 pub fn their_supported_methods(&self) -> Option<Vec<VerificationMethod>> {
346 match &*self.inner.read() {
347 InnerRequest::Requested(r) => Some(r.state.their_methods.clone()),
348 InnerRequest::Ready(r) => Some(r.state.their_methods.clone()),
349 InnerRequest::Transitioned(r) => Some(r.state.ready.their_methods.clone()),
350 InnerRequest::Created(_)
351 | InnerRequest::Passive(_)
352 | InnerRequest::Done(_)
353 | InnerRequest::Cancelled(_) => None,
354 }
355 }
356
357 pub fn our_supported_methods(&self) -> Option<Vec<VerificationMethod>> {
362 match &*self.inner.read() {
363 InnerRequest::Created(r) => Some(r.state.our_methods.clone()),
364 InnerRequest::Ready(r) => Some(r.state.our_methods.clone()),
365 InnerRequest::Transitioned(r) => Some(r.state.ready.our_methods.clone()),
366 InnerRequest::Requested(_)
367 | InnerRequest::Passive(_)
368 | InnerRequest::Done(_)
369 | InnerRequest::Cancelled(_) => None,
370 }
371 }
372
373 pub fn flow_id(&self) -> &FlowId {
375 &self.flow_id
376 }
377
378 pub fn is_self_verification(&self) -> bool {
380 self.account.user_id == self.other_user()
381 }
382
383 pub fn we_started(&self) -> bool {
385 self.we_started
386 }
387
388 pub fn is_done(&self) -> bool {
390 matches!(*self.inner.read(), InnerRequest::Done(_))
391 }
392
393 pub fn is_cancelled(&self) -> bool {
396 matches!(*self.inner.read(), InnerRequest::Cancelled(_))
397 }
398
399 #[cfg(feature = "qrcode")]
402 pub async fn generate_qr_code(&self) -> Result<Option<QrVerification>, CryptoStoreError> {
403 let inner = self.inner.get();
404
405 let ret = if let Some((state, verification)) =
406 inner.generate_qr_code(self.we_started, self.inner.clone().into()).await?
407 {
408 let mut inner = self.inner.write();
409 ObservableWriteGuard::set(&mut inner, InnerRequest::Transitioned(state));
410
411 Some(verification)
412 } else {
413 None
414 };
415
416 Ok(ret)
417 }
418
419 #[cfg(feature = "qrcode")]
427 pub async fn scan_qr_code(
428 &self,
429 data: QrVerificationData,
430 ) -> Result<Option<QrVerification>, ScanError> {
431 let inner = self.inner.read().to_owned();
432
433 let (new_state, qr_verification) = match inner {
434 InnerRequest::Ready(r) => {
435 scan_qr_code(data, &r, &r.state, self.we_started, self.inner.to_owned().into())
436 .await?
437 }
438 InnerRequest::Transitioned(r) => {
439 scan_qr_code(
440 data,
441 &r,
442 &r.state.ready,
443 self.we_started,
444 self.inner.to_owned().into(),
445 )
446 .await?
447 }
448 _ => return Ok(None),
449 };
450
451 if self
455 .verification_cache
456 .get_qr(qr_verification.other_user_id(), qr_verification.flow_id().as_str())
457 .is_some()
458 {
459 self.verification_cache.replace_qr(qr_verification.clone());
460 } else {
461 self.verification_cache.insert_qr(qr_verification.clone());
462 }
463
464 let mut guard = self.inner.write();
465 ObservableWriteGuard::set(&mut guard, InnerRequest::Transitioned(new_state));
466
467 Ok(Some(qr_verification))
468 }
469
470 pub(crate) fn from_request(
471 cache: VerificationCache,
472 store: VerificationStore,
473 sender: &UserId,
474 flow_id: FlowId,
475 content: &RequestContent<'_>,
476 device_data: DeviceData,
477 ) -> Self {
478 let account = store.account.clone();
479
480 Self {
481 verification_cache: cache.clone(),
482 inner: SharedObservable::new(InnerRequest::Requested(
483 RequestState::from_request_event(
484 cache,
485 store,
486 sender,
487 &flow_id,
488 content,
489 device_data,
490 ),
491 )),
492 account,
493 other_user_id: sender.into(),
494 flow_id: flow_id.into(),
495 we_started: false,
496 creation_time: Instant::now().into(),
497 recipient_devices: vec![].into(),
498 }
499 }
500
501 pub fn accept_with_methods(
508 &self,
509 methods: Vec<VerificationMethod>,
510 ) -> Option<OutgoingVerificationRequest> {
511 let mut guard = self.inner.write();
512 let (updated, content) = guard.accept(methods)?;
513
514 ObservableWriteGuard::set(&mut guard, updated);
515
516 let request = match content {
517 OutgoingContent::ToDevice(content) => ToDeviceRequest::with_id(
518 self.other_user(),
519 guard.other_device_id(),
520 &content,
521 TransactionId::new(),
522 )
523 .into(),
524 OutgoingContent::Room(room_id, content) => {
525 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
526 }
527 };
528
529 Some(request)
530 }
531
532 pub fn accept(&self) -> Option<OutgoingVerificationRequest> {
544 self.accept_with_methods(SUPPORTED_METHODS.to_vec())
545 }
546
547 pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
549 self.cancel_with_code(CancelCode::User)
550 }
551
552 fn cancel_with_code(&self, cancel_code: CancelCode) -> Option<OutgoingVerificationRequest> {
553 let mut guard = self.inner.write();
554
555 let send_to_everyone = self.we_started() && matches!(*guard, InnerRequest::Created(_));
556 let other_device = guard.other_device_id();
557
558 if let Some(updated) = guard.cancel(true, &cancel_code) {
559 ObservableWriteGuard::set(&mut guard, updated);
560 }
561
562 let content = as_variant!(&*guard, InnerRequest::Cancelled(c) => {
563 c.state.as_content(self.flow_id())
564 });
565
566 let request = content.map(|c| match c {
567 OutgoingContent::ToDevice(content) => {
568 if send_to_everyone {
569 ToDeviceRequest::for_recipients(
570 self.other_user(),
571 self.recipient_devices.to_vec(),
572 &content,
573 TransactionId::new(),
574 )
575 .into()
576 } else {
577 ToDeviceRequest::with_id(
578 self.other_user(),
579 other_device,
580 &content,
581 TransactionId::new(),
582 )
583 .into()
584 }
585 }
586 OutgoingContent::Room(room_id, content) => {
587 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
588 }
589 });
590
591 drop(guard);
592
593 if let Some(verification) =
594 self.verification_cache.get(self.other_user(), self.flow_id().as_str())
595 {
596 match verification {
597 Verification::SasV1(s) => s.cancel_with_code(cancel_code),
598 #[cfg(feature = "qrcode")]
599 Verification::QrV1(q) => q.cancel_with_code(cancel_code),
600 };
601 }
602
603 request
604 }
605
606 pub(crate) fn cancel_if_timed_out(&self) -> Option<OutgoingVerificationRequest> {
607 if self.is_cancelled() || self.is_done() {
608 None
609 } else if self.timed_out() {
610 let request = self.cancel_with_code(CancelCode::Timeout);
611
612 if self.is_passive() {
613 None
614 } else {
615 trace!(
616 other_user = self.other_user().as_str(),
617 flow_id = self.flow_id().as_str(),
618 "Timing a verification request out"
619 );
620 request
621 }
622 } else {
623 None
624 }
625 }
626
627 pub(crate) fn cancel_for_other_devices(
647 &self,
648 code: CancelCode,
649 filter_device: Option<&DeviceId>,
650 ) -> Option<ToDeviceRequest> {
651 let cancelled = Cancelled::new(true, code);
652 let cancel_content = cancelled.as_content(self.flow_id());
653
654 let OutgoingContent::ToDevice(c) = cancel_content else { return None };
655 let recip_devices: Vec<OwnedDeviceId> = self
656 .recipient_devices
657 .iter()
658 .filter(|&d| filter_device.is_none_or(|device| **d != *device))
659 .cloned()
660 .collect();
661
662 if recip_devices.is_empty() && filter_device.is_some() {
663 return None;
668 }
669
670 let recipient = self.other_user();
671 Some(ToDeviceRequest::for_recipients(recipient, recip_devices, &c, TransactionId::new()))
672 }
673
674 pub(crate) fn receive_ready(
675 &self,
676 sender: &UserId,
677 content: &ReadyContent<'_>,
678 from_device_data: DeviceData,
679 ) {
680 let mut guard = self.inner.write();
681
682 match &*guard {
683 InnerRequest::Created(s) => {
684 let new_value =
685 InnerRequest::Ready(s.clone().into_ready(sender, content, from_device_data));
686 ObservableWriteGuard::set(&mut guard, new_value);
687
688 if let Some(request) =
689 self.cancel_for_other_devices(CancelCode::Accepted, Some(content.from_device()))
690 {
691 self.verification_cache.add_verification_request(request.into());
692 }
693 }
694 InnerRequest::Requested(s) => {
695 if sender == self.own_user_id() && content.from_device() != self.account.device_id {
696 let new_value = InnerRequest::Passive(s.clone().into_passive(content));
697 ObservableWriteGuard::set(&mut guard, new_value);
698 }
699 }
700 InnerRequest::Ready(_)
701 | InnerRequest::Transitioned(_)
702 | InnerRequest::Passive(_)
703 | InnerRequest::Done(_)
704 | InnerRequest::Cancelled(_) => {}
705 }
706 }
707
708 pub(crate) async fn receive_start(
709 &self,
710 sender: &UserId,
711 content: &StartContent<'_>,
712 ) -> Result<(), CryptoStoreError> {
713 let inner = self.inner.get();
714
715 match &inner {
716 InnerRequest::Created(_)
717 | InnerRequest::Requested(_)
718 | InnerRequest::Passive(_)
719 | InnerRequest::Done(_)
720 | InnerRequest::Cancelled(_) => {
721 warn!(
722 ?sender,
723 device_id = content.from_device().as_str(),
724 "Received a key verification start event but we're not yet in the ready state"
725 );
726 Ok(())
727 }
728 InnerRequest::Ready(s) => {
729 let s = s.clone();
730
731 if let Some(new_state) = s
732 .receive_start(sender, content, self.we_started, self.inner.clone().into())
733 .await?
734 {
735 let mut inner = self.inner.write();
736 ObservableWriteGuard::set(&mut inner, InnerRequest::Transitioned(new_state));
737 }
738
739 Ok(())
740 }
741 InnerRequest::Transitioned(s) => {
742 let s = s.clone();
751
752 if let Some(new_state) = s
753 .receive_start(sender, content, self.we_started, self.inner.clone().into())
754 .await?
755 {
756 let mut inner = self.inner.write();
757 ObservableWriteGuard::set(&mut inner, InnerRequest::Transitioned(new_state));
758 }
759
760 Ok(())
761 }
762 }
763 }
764
765 pub(crate) fn receive_done(&self, sender: &UserId, content: &DoneContent<'_>) {
766 if sender == self.other_user() {
767 trace!(
768 other_user = ?self.other_user(),
769 flow_id = self.flow_id().as_str(),
770 "Marking a verification request as done"
771 );
772
773 let mut guard = self.inner.write();
774 if let Some(updated) = guard.receive_done(content) {
775 ObservableWriteGuard::set(&mut guard, updated);
776 }
777 }
778 }
779
780 pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) {
781 if sender != self.other_user() {
782 return;
783 }
784
785 trace!(
786 ?sender,
787 code = content.cancel_code().as_str(),
788 "Cancelling a verification request, other user has cancelled"
789 );
790 let mut guard = self.inner.write();
791 if let Some(updated) = guard.cancel(false, content.cancel_code()) {
792 ObservableWriteGuard::set(&mut guard, updated);
793 }
794
795 if self.we_started()
796 && let Some(request) =
797 self.cancel_for_other_devices(content.cancel_code().to_owned(), None)
798 {
799 self.verification_cache.add_verification_request(request.into());
800 }
801 }
802
803 fn start_sas_helper(
804 &self,
805 new_state: RequestState<Transitioned>,
806 sas: Sas,
807 content: OutgoingContent,
808 other_device_id: DeviceIdOrAllDevices,
809 ) -> Option<(Sas, OutgoingVerificationRequest)> {
810 cfg_if::cfg_if! {
814 if #[cfg(feature = "qrcode")] {
815 if self.verification_cache.get_qr(sas.other_user_id(), sas.flow_id().as_str()).is_some() {
816 debug!(
817 user_id = ?self.other_user(),
818 flow_id = self.flow_id().as_str(),
819 "We have an ongoing QR verification, replacing with SAS"
820 );
821 self.verification_cache.replace(sas.clone().into())
822 } else {
823 self.verification_cache.insert_sas(sas.clone());
824 }
825 } else {
826 self.verification_cache.insert_sas(sas.clone());
827 }
828 }
829
830 let request = match content {
831 OutgoingContent::ToDevice(content) => ToDeviceRequest::with_id(
832 self.other_user(),
833 other_device_id,
834 &content,
835 TransactionId::new(),
836 )
837 .into(),
838 OutgoingContent::Room(room_id, content) => {
839 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
840 }
841 };
842
843 let mut guard = self.inner.write();
844 ObservableWriteGuard::set(&mut guard, InnerRequest::Transitioned(new_state));
845
846 Some((sas, request))
847 }
848
849 pub async fn start_sas(
851 &self,
852 ) -> Result<Option<(Sas, OutgoingVerificationRequest)>, CryptoStoreError> {
853 let inner = self.inner.get();
854 let other_device_id = inner.other_device_id();
855
856 Ok(match &inner {
857 InnerRequest::Ready(s) => {
858 if let Some((new_state, sas, content)) =
859 s.start_sas(self.we_started, self.inner.clone().into()).await?
860 {
861 self.start_sas_helper(new_state, sas, content, other_device_id)
862 } else {
863 None
864 }
865 }
866 InnerRequest::Transitioned(s) => {
867 if let Some((new_state, sas, content)) =
868 s.start_sas(self.we_started, self.inner.clone().into()).await?
869 {
870 self.start_sas_helper(new_state, sas, content, other_device_id)
871 } else {
872 None
873 }
874 }
875 _ => None,
876 })
877 }
878
879 pub fn changes(&self) -> impl Stream<Item = VerificationRequestState> + use<> {
884 self.inner.subscribe().map(|s| (&s).into())
885 }
886
887 pub fn state(&self) -> VerificationRequestState {
892 (&*self.inner.read()).into()
893 }
894}
895
896#[derive(Clone, Debug)]
897enum InnerRequest {
898 Created(RequestState<Created>),
899 Requested(RequestState<Requested>),
900 Ready(RequestState<Ready>),
901 Transitioned(RequestState<Transitioned>),
902 Passive(RequestState<Passive>),
903 #[allow(dead_code)] Done(RequestState<Done>),
905 Cancelled(RequestState<Cancelled>),
906}
907
908impl InnerRequest {
909 fn other_device_id(&self) -> DeviceIdOrAllDevices {
910 match self {
911 InnerRequest::Created(_) => DeviceIdOrAllDevices::AllDevices,
912 InnerRequest::Requested(r) => {
913 DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned())
914 }
915 InnerRequest::Ready(r) => {
916 DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned())
917 }
918 InnerRequest::Transitioned(r) => DeviceIdOrAllDevices::DeviceId(
919 r.state.ready.other_device_data.device_id().to_owned(),
920 ),
921 InnerRequest::Passive(_) => DeviceIdOrAllDevices::AllDevices,
922 InnerRequest::Done(_) => DeviceIdOrAllDevices::AllDevices,
923 InnerRequest::Cancelled(_) => DeviceIdOrAllDevices::AllDevices,
924 }
925 }
926
927 fn accept(&self, methods: Vec<VerificationMethod>) -> Option<(InnerRequest, OutgoingContent)> {
928 let InnerRequest::Requested(s) = self else { return None };
929 let (state, content) = s.clone().accept(methods);
930
931 Some((InnerRequest::Ready(state), content))
932 }
933
934 fn receive_done(&self, content: &DoneContent<'_>) -> Option<InnerRequest> {
935 let state = InnerRequest::Done(match self {
936 InnerRequest::Transitioned(s) => s.clone().into_done(content),
937 InnerRequest::Passive(s) => s.clone().into_done(content),
938 InnerRequest::Done(_)
939 | InnerRequest::Ready(_)
940 | InnerRequest::Created(_)
941 | InnerRequest::Requested(_)
942 | InnerRequest::Cancelled(_) => return None,
943 });
944
945 Some(state)
946 }
947
948 fn cancel(&self, cancelled_by_us: bool, cancel_code: &CancelCode) -> Option<InnerRequest> {
949 let print_info = || {
950 trace!(
951 cancelled_by_us,
952 code = cancel_code.as_str(),
953 "Verification request going into the cancelled state"
954 );
955 };
956
957 let state = InnerRequest::Cancelled(match self {
958 InnerRequest::Created(s) => {
959 print_info();
960 s.clone().into_canceled(cancelled_by_us, cancel_code)
961 }
962 InnerRequest::Requested(s) => {
963 print_info();
964 s.clone().into_canceled(cancelled_by_us, cancel_code)
965 }
966 InnerRequest::Ready(s) => {
967 print_info();
968 s.clone().into_canceled(cancelled_by_us, cancel_code)
969 }
970 InnerRequest::Transitioned(s) => {
971 print_info();
972 s.clone().into_canceled(cancelled_by_us, cancel_code)
973 }
974 InnerRequest::Passive(_) | InnerRequest::Done(_) | InnerRequest::Cancelled(_) => {
975 return None;
976 }
977 });
978
979 Some(state)
980 }
981
982 #[cfg(feature = "qrcode")]
983 async fn generate_qr_code(
984 &self,
985 we_started: bool,
986 request_handle: RequestHandle,
987 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
988 match self {
989 InnerRequest::Created(_)
990 | InnerRequest::Requested(_)
991 | InnerRequest::Passive(_)
992 | InnerRequest::Done(_)
993 | InnerRequest::Cancelled(_) => Ok(None),
994 InnerRequest::Ready(s) => s.generate_qr_code(we_started, request_handle).await,
995 InnerRequest::Transitioned(s) => s.generate_qr_code(we_started, request_handle).await,
996 }
997 }
998}
999
1000#[derive(Clone, Debug)]
1001struct RequestState<S: Clone> {
1002 verification_cache: VerificationCache,
1003 store: VerificationStore,
1004 flow_id: Arc<FlowId>,
1005
1006 pub other_user_id: OwnedUserId,
1008
1009 state: S,
1011}
1012
1013impl<S: Clone> RequestState<S> {
1014 fn into_done(self, _: &DoneContent<'_>) -> RequestState<Done> {
1015 RequestState::<Done> {
1016 verification_cache: self.verification_cache,
1017 store: self.store,
1018 flow_id: self.flow_id,
1019 other_user_id: self.other_user_id,
1020 state: Done {},
1021 }
1022 }
1023
1024 fn into_canceled(
1025 self,
1026 cancelled_by_us: bool,
1027 cancel_code: &CancelCode,
1028 ) -> RequestState<Cancelled> {
1029 RequestState::<Cancelled> {
1030 verification_cache: self.verification_cache,
1031 store: self.store,
1032 flow_id: self.flow_id,
1033 other_user_id: self.other_user_id,
1034 state: Cancelled::new(cancelled_by_us, cancel_code.clone()),
1035 }
1036 }
1037}
1038
1039impl RequestState<Created> {
1040 fn new(
1041 cache: VerificationCache,
1042 store: VerificationStore,
1043 other_user_id: &UserId,
1044 flow_id: &FlowId,
1045 methods: Option<Vec<VerificationMethod>>,
1046 ) -> Self {
1047 let our_methods = methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec());
1048
1049 Self {
1050 other_user_id: other_user_id.to_owned(),
1051 state: Created { our_methods },
1052 verification_cache: cache,
1053 store,
1054 flow_id: flow_id.to_owned().into(),
1055 }
1056 }
1057
1058 fn into_ready(
1059 self,
1060 _sender: &UserId,
1061 content: &ReadyContent<'_>,
1062 from_device_data: DeviceData,
1063 ) -> RequestState<Ready> {
1064 RequestState {
1066 flow_id: self.flow_id,
1067 verification_cache: self.verification_cache,
1068 store: self.store,
1069 other_user_id: self.other_user_id,
1070 state: Ready {
1071 their_methods: content.methods().to_owned(),
1072 our_methods: self.state.our_methods,
1073 other_device_data: from_device_data,
1074 },
1075 }
1076 }
1077}
1078
1079#[derive(Clone, Debug)]
1080struct Created {
1081 pub our_methods: Vec<VerificationMethod>,
1083}
1084
1085#[derive(Clone, Debug)]
1086struct Requested {
1087 pub their_methods: Vec<VerificationMethod>,
1089
1090 pub other_device_data: DeviceData,
1093}
1094
1095impl RequestState<Requested> {
1096 fn from_request_event(
1097 cache: VerificationCache,
1098 store: VerificationStore,
1099 sender: &UserId,
1100 flow_id: &FlowId,
1101 content: &RequestContent<'_>,
1102 device_data: DeviceData,
1103 ) -> RequestState<Requested> {
1104 RequestState {
1106 store,
1107 verification_cache: cache,
1108 flow_id: flow_id.to_owned().into(),
1109 other_user_id: sender.to_owned(),
1110 state: Requested {
1111 their_methods: content.methods().to_owned(),
1112 other_device_data: device_data,
1113 },
1114 }
1115 }
1116
1117 fn into_passive(self, content: &ReadyContent<'_>) -> RequestState<Passive> {
1118 RequestState {
1119 flow_id: self.flow_id,
1120 verification_cache: self.verification_cache,
1121 store: self.store,
1122 other_user_id: self.other_user_id,
1123 state: Passive { other_device_id: content.from_device().to_owned() },
1124 }
1125 }
1126
1127 fn accept(self, methods: Vec<VerificationMethod>) -> (RequestState<Ready>, OutgoingContent) {
1128 let state = RequestState {
1129 store: self.store,
1130 verification_cache: self.verification_cache,
1131 flow_id: self.flow_id.clone(),
1132 other_user_id: self.other_user_id,
1133 state: Ready {
1134 their_methods: self.state.their_methods,
1135 our_methods: methods.clone(),
1136 other_device_data: self.state.other_device_data,
1137 },
1138 };
1139
1140 let content = match self.flow_id.as_ref() {
1141 FlowId::ToDevice(i) => AnyToDeviceEventContent::KeyVerificationReady(
1142 ToDeviceKeyVerificationReadyEventContent::new(
1143 state.store.account.device_id.clone(),
1144 methods,
1145 i.to_owned(),
1146 ),
1147 )
1148 .into(),
1149 FlowId::InRoom(r, e) => (
1150 r.to_owned(),
1151 AnyMessageLikeEventContent::KeyVerificationReady(
1152 KeyVerificationReadyEventContent::new(
1153 state.store.account.device_id.clone(),
1154 methods,
1155 Reference::new(e.to_owned()),
1156 ),
1157 ),
1158 )
1159 .into(),
1160 };
1161
1162 (state, content)
1163 }
1164}
1165
1166#[derive(Clone, Debug)]
1167struct Ready {
1168 pub their_methods: Vec<VerificationMethod>,
1170
1171 pub our_methods: Vec<VerificationMethod>,
1173
1174 pub other_device_data: DeviceData,
1177}
1178
1179#[cfg(feature = "qrcode")]
1180async fn scan_qr_code<T: Clone>(
1181 data: QrVerificationData,
1182 request_state: &RequestState<T>,
1183 state: &Ready,
1184 we_started: bool,
1185 request_handle: RequestHandle,
1186) -> Result<(RequestState<Transitioned>, QrVerification), ScanError> {
1187 let verification = QrVerification::from_scan(
1188 request_state.store.to_owned(),
1189 request_state.other_user_id.to_owned(),
1190 state.other_device_data.device_id().to_owned(),
1191 request_state.flow_id.as_ref().to_owned(),
1192 data,
1193 we_started,
1194 Some(request_handle),
1195 )
1196 .await?;
1197
1198 let new_state = RequestState {
1199 verification_cache: request_state.verification_cache.to_owned(),
1200 store: request_state.store.to_owned(),
1201 flow_id: request_state.flow_id.to_owned(),
1202 other_user_id: request_state.other_user_id.to_owned(),
1203 state: Transitioned {
1204 ready: state.to_owned(),
1205 verification: verification.to_owned().into(),
1206 other_device_data: state.other_device_data.to_owned(),
1207 },
1208 };
1209
1210 Ok((new_state, verification))
1211}
1212
1213#[cfg(feature = "qrcode")]
1214async fn generate_qr_code<T: Clone>(
1215 request_state: &RequestState<T>,
1216 state: &Ready,
1217 we_started: bool,
1218 request_handle: RequestHandle,
1219) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1220 use crate::UserIdentityData;
1221
1222 if !state.our_methods.contains(&VerificationMethod::QrCodeShowV1)
1225 || !state.their_methods.contains(&VerificationMethod::QrCodeScanV1)
1226 {
1227 return Ok(None);
1228 }
1229
1230 let identities = request_state.store.get_identities(state.other_device_data.clone()).await?;
1231
1232 let verification = if let Some(identity) = &identities.identity_being_verified {
1233 match &identity {
1234 UserIdentityData::Own(i) => {
1235 if let Some(master_key) = i.master_key().get_first_key() {
1236 if identities.can_sign_devices().await {
1237 if let Some(device_key) = identities.other_device().ed25519_key() {
1238 Some(QrVerification::new_self(
1239 request_state.flow_id.as_ref().to_owned(),
1240 master_key.to_owned(),
1241 device_key.to_owned(),
1242 identities,
1243 we_started,
1244 Some(request_handle),
1245 ))
1246 } else {
1247 warn!(
1248 user_id = ?request_state.other_user_id,
1249 device_id = ?state.other_device_data.device_id(),
1250 "Can't create a QR code, the other device \
1251 doesn't have a valid device key"
1252 );
1253 None
1254 }
1255 } else {
1256 Some(QrVerification::new_self_no_master(
1257 request_state.store.clone(),
1258 request_state.flow_id.as_ref().to_owned(),
1259 master_key.to_owned(),
1260 identities,
1261 we_started,
1262 Some(request_handle),
1263 ))
1264 }
1265 } else {
1266 warn!(
1267 user_id = ?request_state.other_user_id,
1268 device_id = ?state.other_device_data.device_id(),
1269 "Can't create a QR code, our cross signing identity \
1270 doesn't contain a valid master key"
1271 );
1272 None
1273 }
1274 }
1275 UserIdentityData::Other(i) => {
1276 if let Some(other_master) = i.master_key().get_first_key() {
1277 if let Some(own_master) = identities
1281 .private_identity
1282 .master_public_key()
1283 .await
1284 .and_then(|m| m.get_first_key().map(|m| m.to_owned()))
1285 {
1286 Some(QrVerification::new_cross(
1287 request_state.flow_id.as_ref().to_owned(),
1288 own_master,
1289 other_master.to_owned(),
1290 identities,
1291 we_started,
1292 Some(request_handle),
1293 ))
1294 } else {
1295 warn!(
1296 user_id = ?request_state.other_user_id,
1297 device_id = ?state.other_device_data.device_id(),
1298 "Can't create a QR code, we don't trust our own \
1299 master key"
1300 );
1301 None
1302 }
1303 } else {
1304 warn!(
1305 user_id = ?request_state.other_user_id,
1306 device_id = ?state.other_device_data.device_id(),
1307 "Can't create a QR code, the user's identity \
1308 doesn't have a valid master key"
1309 );
1310 None
1311 }
1312 }
1313 }
1314 } else {
1315 warn!(
1316 user_id = ?request_state.other_user_id,
1317 device_id = ?state.other_device_data.device_id(),
1318 "Can't create a QR code, the user doesn't have a valid cross \
1319 signing identity."
1320 );
1321
1322 None
1323 };
1324
1325 if let Some(verification) = verification {
1326 let new_state = RequestState {
1327 verification_cache: request_state.verification_cache.to_owned(),
1328 store: request_state.store.to_owned(),
1329 flow_id: request_state.flow_id.to_owned(),
1330 other_user_id: request_state.other_user_id.to_owned(),
1331 state: Transitioned {
1332 ready: state.to_owned(),
1333 verification: verification.to_owned().into(),
1334 other_device_data: state.other_device_data.to_owned(),
1335 },
1336 };
1337
1338 request_state.verification_cache.insert_qr(verification.to_owned());
1339
1340 Ok(Some((new_state, verification)))
1341 } else {
1342 Ok(None)
1343 }
1344}
1345
1346async fn receive_start<T: Clone>(
1347 sender: &UserId,
1348 content: &StartContent<'_>,
1349 we_started: bool,
1350 request_handle: RequestHandle,
1351 request_state: &RequestState<T>,
1352 state: &Ready,
1353) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1354 info!(
1355 ?sender,
1356 device = ?content.from_device(),
1357 method = ?content.method(),
1358 "Received a new verification start event",
1359 );
1360
1361 let other_device_data = state.other_device_data.clone();
1362 let identities = request_state.store.get_identities(other_device_data.clone()).await?;
1363 let own_user_id = &request_state.store.account.user_id;
1364 let own_device_id = &request_state.store.account.device_id;
1365
1366 match content.method() {
1367 StartMethod::SasV1(_) => {
1368 match Sas::from_start_event(
1369 (*request_state.flow_id).to_owned(),
1370 content,
1371 identities,
1372 Some(request_handle),
1373 we_started,
1374 ) {
1375 Ok(new) => {
1376 let old_verification = request_state
1377 .verification_cache
1378 .get(sender, request_state.flow_id.as_str());
1379 match old_verification {
1380 Some(Verification::SasV1(_old)) => {
1381 use std::cmp::Ordering;
1387 if !matches!(
1388 (
1389 sender.cmp(own_user_id),
1390 other_device_data.device_id().cmp(own_device_id)
1391 ),
1392 (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater)
1393 ) {
1394 info!(
1395 "Started a new SAS verification, replacing an already started one."
1396 );
1397 request_state.verification_cache.replace_sas(new.to_owned());
1398 Ok(Some(state.to_transitioned(request_state, new.into())))
1399 } else {
1400 info!(
1401 "Ignored incoming SAS verification from lexicographically larger user/device ID."
1402 );
1403 Ok(None)
1404 }
1405 }
1406 #[cfg(feature = "qrcode")]
1407 Some(Verification::QrV1(old)) => {
1408 if let QrVerificationState::Started = old.state() {
1412 info!("Transitioned from QR display to SAS");
1415 request_state.verification_cache.replace_sas(new.to_owned());
1416 Ok(Some(state.to_transitioned(request_state, new.into())))
1417 } else {
1418 warn!(qr_state = ?old.state(), "Invalid transition from QR to SAS");
1425 request_state.verification_cache.insert_sas(new.to_owned());
1426 Ok(Some(state.to_transitioned(request_state, new.into())))
1427 }
1428 }
1429 None => {
1430 info!("Started a new SAS verification.");
1431 request_state.verification_cache.insert_sas(new.to_owned());
1432 Ok(Some(state.to_transitioned(request_state, new.into())))
1433 }
1434 }
1435 }
1436 Err(c) => {
1437 warn!(
1438 user_id = ?other_device_data.user_id(),
1439 device_id = ?other_device_data.device_id(),
1440 content = ?c,
1441 "Can't start key verification, canceling.",
1442 );
1443 request_state.verification_cache.queue_up_content(
1444 other_device_data.user_id(),
1445 other_device_data.device_id(),
1446 c,
1447 None,
1448 );
1449
1450 Ok(None)
1451 }
1452 }
1453 }
1454 #[cfg(feature = "qrcode")]
1455 StartMethod::ReciprocateV1(_) => {
1456 if let Some(qr_verification) =
1457 request_state.verification_cache.get_qr(sender, content.flow_id())
1458 {
1459 if let Some(request) = qr_verification.receive_reciprocation(content) {
1460 request_state.verification_cache.add_request(request.into())
1461 }
1462 debug!(
1463 sender = ?identities.device_being_verified.user_id(),
1464 device_id = ?identities.device_being_verified.device_id(),
1465 verification = ?qr_verification,
1466 "Received a QR code reciprocation"
1467 );
1468
1469 Ok(None)
1470 } else {
1471 warn!("Received a QR code reciprocation for an unknown flow");
1472 Ok(None)
1473 }
1474 }
1475 m => {
1476 warn!(method = ?m, "Received a key verification start event with an unsupported method");
1477 Ok(None)
1478 }
1479 }
1480}
1481
1482async fn start_sas<T: Clone>(
1483 request_state: &RequestState<T>,
1484 state: &Ready,
1485 we_started: bool,
1486 request_handle: RequestHandle,
1487) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1488 if !state.their_methods.contains(&VerificationMethod::SasV1) {
1489 return Ok(None);
1490 }
1491
1492 let identities = request_state.store.get_identities(state.other_device_data.clone()).await?;
1493
1494 let (state, sas, content) = match request_state.flow_id.as_ref() {
1495 FlowId::ToDevice(t) => {
1496 let (sas, content) =
1497 Sas::start(identities, t.to_owned(), we_started, Some(request_handle), None);
1498
1499 let state = Transitioned {
1500 ready: state.to_owned(),
1501 verification: sas.to_owned().into(),
1502 other_device_data: state.other_device_data.to_owned(),
1503 };
1504
1505 (state, sas, content)
1506 }
1507 FlowId::InRoom(r, e) => {
1508 let (sas, content) = Sas::start_in_room(
1509 e.to_owned(),
1510 r.to_owned(),
1511 identities,
1512 we_started,
1513 request_handle,
1514 );
1515 let state = Transitioned {
1516 ready: state.to_owned(),
1517 verification: sas.to_owned().into(),
1518 other_device_data: state.other_device_data.to_owned(),
1519 };
1520 (state, sas, content)
1521 }
1522 };
1523
1524 let state = RequestState {
1525 verification_cache: request_state.verification_cache.to_owned(),
1526 store: request_state.store.to_owned(),
1527 flow_id: request_state.flow_id.to_owned(),
1528 other_user_id: request_state.other_user_id.to_owned(),
1529 state,
1530 };
1531
1532 Ok(Some((state, sas, content)))
1533}
1534
1535impl RequestState<Ready> {
1536 #[cfg(feature = "qrcode")]
1537 async fn generate_qr_code(
1538 &self,
1539 we_started: bool,
1540 request_handle: RequestHandle,
1541 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1542 generate_qr_code(self, &self.state, we_started, request_handle).await
1543 }
1544
1545 async fn receive_start(
1546 &self,
1547 sender: &UserId,
1548 content: &StartContent<'_>,
1549 we_started: bool,
1550 request_handle: RequestHandle,
1551 ) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1552 receive_start(sender, content, we_started, request_handle, self, &self.state).await
1553 }
1554
1555 async fn start_sas(
1556 &self,
1557 we_started: bool,
1558 request_handle: RequestHandle,
1559 ) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1560 start_sas(self, &self.state, we_started, request_handle).await
1561 }
1562}
1563
1564impl Ready {
1565 fn to_transitioned<T: Clone>(
1566 &self,
1567 request_state: &RequestState<T>,
1568 verification: Verification,
1569 ) -> RequestState<Transitioned> {
1570 RequestState {
1571 verification_cache: request_state.verification_cache.to_owned(),
1572 store: request_state.store.to_owned(),
1573 flow_id: request_state.flow_id.to_owned(),
1574 other_user_id: request_state.other_user_id.to_owned(),
1575 state: Transitioned {
1576 ready: self.clone(),
1577 verification,
1578 other_device_data: self.other_device_data.clone(),
1579 },
1580 }
1581 }
1582}
1583
1584#[derive(Clone, Debug)]
1585struct Transitioned {
1586 ready: Ready,
1587 verification: Verification,
1588 other_device_data: DeviceData,
1589}
1590
1591impl RequestState<Transitioned> {
1592 #[cfg(feature = "qrcode")]
1593 async fn generate_qr_code(
1594 &self,
1595 we_started: bool,
1596 request_handle: RequestHandle,
1597 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1598 generate_qr_code(self, &self.state.ready, we_started, request_handle).await
1599 }
1600
1601 async fn receive_start(
1602 &self,
1603 sender: &UserId,
1604 content: &StartContent<'_>,
1605 we_started: bool,
1606 request_handle: RequestHandle,
1607 ) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1608 receive_start(sender, content, we_started, request_handle, self, &self.state.ready).await
1609 }
1610
1611 async fn start_sas(
1612 &self,
1613 we_started: bool,
1614 request_handle: RequestHandle,
1615 ) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1616 start_sas(self, &self.state.ready, we_started, request_handle).await
1617 }
1618}
1619
1620#[derive(Clone, Debug)]
1621struct Passive {
1622 #[allow(dead_code)]
1624 pub other_device_id: OwnedDeviceId,
1625}
1626
1627#[derive(Clone, Debug)]
1628struct Done {}
1629
1630#[cfg(test)]
1631mod tests {
1632
1633 use std::time::Duration;
1634
1635 use assert_matches::assert_matches;
1636 #[cfg(feature = "qrcode")]
1637 use matrix_sdk_qrcode::QrVerificationData;
1638 use matrix_sdk_test::async_test;
1639 use ruma::{
1640 UserId, event_id, events::key::verification::VerificationMethod, owned_event_id,
1641 owned_room_id, room_id, to_device::DeviceIdOrAllDevices,
1642 };
1643 use strass::assert_let;
1644
1645 use super::VerificationRequest;
1646 use crate::{
1647 DeviceData, VerificationRequestState,
1648 types::requests::OutgoingVerificationRequest,
1649 verification::{
1650 FlowId, Verification, VerificationStore,
1651 cache::VerificationCache,
1652 event_enums::{
1653 CancelContent, OutgoingContent, ReadyContent, RequestContent, StartContent,
1654 },
1655 tests::{alice_id, bob_id, setup_stores},
1656 },
1657 };
1658
1659 #[async_test]
1660 async fn test_request_accepting() {
1661 let event_id = owned_event_id!("$1234localhost");
1662 let room_id = owned_room_id!("!test:localhost");
1663
1664 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1665
1666 let alice_device_data = DeviceData::from_account(&alice);
1667 let bob_device_data = DeviceData::from_account(&bob);
1668
1669 let content = VerificationRequest::request(
1670 &bob_store.account.user_id,
1671 &bob_store.account.device_id,
1672 alice_id(),
1673 None,
1674 );
1675
1676 let flow_id = FlowId::InRoom(room_id, event_id);
1677
1678 let bob_request = VerificationRequest::new(
1679 VerificationCache::new(),
1680 bob_store,
1681 flow_id.clone(),
1682 alice_id(),
1683 vec![],
1684 None,
1685 );
1686
1687 assert_matches!(bob_request.state(), VerificationRequestState::Created { .. });
1688 assert!(bob_request.time_remaining() <= Duration::from_secs(600)); assert!(bob_request.time_remaining() > Duration::from_secs(540)); #[allow(clippy::needless_borrow)]
1692 let alice_request = VerificationRequest::from_request(
1693 VerificationCache::new(),
1694 alice_store,
1695 bob_id(),
1696 flow_id,
1697 &(&content).into(),
1698 bob_device_data,
1699 );
1700
1701 assert_matches!(alice_request.state(), VerificationRequestState::Requested { .. });
1702
1703 let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap();
1704 let content = ReadyContent::try_from(&content).unwrap();
1705
1706 bob_request.receive_ready(alice_id(), &content, alice_device_data);
1707
1708 assert_matches!(bob_request.state(), VerificationRequestState::Ready { .. });
1709 assert_matches!(alice_request.state(), VerificationRequestState::Ready { .. });
1710 assert!(bob_request.is_ready());
1711 assert!(alice_request.is_ready());
1712 }
1713
1714 #[async_test]
1715 async fn test_request_refusal_to_device() {
1716 let (_alice, alice_store, bob, bob_store) = setup_stores().await;
1719 let bob_device = DeviceData::from_account(&bob);
1720
1721 let bob_request = build_test_request(&bob_store, alice_id(), None);
1723 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1724
1725 let outgoing_request = alice_request.cancel().unwrap();
1726
1727 {
1729 assert_let!(
1730 OutgoingVerificationRequest::ToDevice(to_device_request) = &outgoing_request
1731 );
1732
1733 assert_eq!(to_device_request.messages.len(), 1);
1734 let device_ids: Vec<&DeviceIdOrAllDevices> =
1735 to_device_request.messages.values().next().unwrap().keys().collect();
1736 assert_eq!(device_ids.len(), 1);
1737
1738 assert_let!(DeviceIdOrAllDevices::DeviceId(device_id) = &device_ids[0]);
1739 assert_eq!(device_id, bob_device.device_id());
1740 }
1741
1742 let content = OutgoingContent::try_from(outgoing_request).unwrap();
1743 let content = CancelContent::try_from(&content).unwrap();
1744
1745 bob_request.receive_cancel(alice_id(), &content);
1746
1747 assert_matches!(bob_request.state(), VerificationRequestState::Cancelled { .. });
1748 assert_matches!(alice_request.state(), VerificationRequestState::Cancelled { .. });
1749 }
1750
1751 #[async_test]
1752 async fn test_requesting_until_sas() {
1753 let event_id = event_id!("$1234localhost");
1754 let room_id = room_id!("!test:localhost");
1755
1756 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1757
1758 let alice_device_data = DeviceData::from_account(&alice);
1759 let bob_device_data = DeviceData::from_account(&bob);
1760
1761 let content = VerificationRequest::request(
1762 &bob_store.account.user_id,
1763 &bob_store.account.device_id,
1764 alice_id(),
1765 None,
1766 );
1767
1768 let flow_id = FlowId::from((room_id, event_id));
1769
1770 let bob_request = VerificationRequest::new(
1771 VerificationCache::new(),
1772 bob_store,
1773 flow_id.clone(),
1774 alice_id(),
1775 vec![],
1776 None,
1777 );
1778
1779 #[allow(clippy::needless_borrow)]
1780 let alice_request = VerificationRequest::from_request(
1781 VerificationCache::new(),
1782 alice_store,
1783 bob_id(),
1784 flow_id,
1785 &(&content).into(),
1786 bob_device_data.clone(),
1787 );
1788
1789 do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None);
1790
1791 let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
1792
1793 let content: OutgoingContent = request.try_into().unwrap();
1794 let content = StartContent::try_from(&content).unwrap();
1795 let flow_id = content.flow_id().to_owned();
1796 alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap();
1797 let alice_sas =
1798 alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap();
1799
1800 assert_let!(
1801 VerificationRequestState::Transitioned {
1802 verification: Verification::SasV1(_),
1803 other_device_data
1804 } = alice_request.state()
1805 );
1806
1807 assert_eq!(bob_device_data, other_device_data);
1808
1809 assert_let!(
1810 VerificationRequestState::Transitioned {
1811 verification: Verification::SasV1(_),
1812 other_device_data
1813 } = bob_request.state()
1814 );
1815
1816 assert_eq!(alice_device_data, other_device_data);
1817
1818 assert!(!bob_sas.is_cancelled());
1819 assert!(!alice_sas.is_cancelled());
1820 }
1821
1822 #[async_test]
1823 async fn test_requesting_until_sas_to_device() {
1824 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1825
1826 let alice_device_data = DeviceData::from_account(&alice);
1827 let bob_device_data = DeviceData::from_account(&bob);
1828
1829 let bob_request = build_test_request(&bob_store, alice_id(), None);
1831 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1832 do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None);
1833
1834 let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
1835
1836 let content: OutgoingContent = request.try_into().unwrap();
1837 let content = StartContent::try_from(&content).unwrap();
1838 let flow_id = content.flow_id().to_owned();
1839 alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap();
1840 let alice_sas =
1841 alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap();
1842
1843 assert_let!(
1844 VerificationRequestState::Transitioned {
1845 verification: Verification::SasV1(_),
1846 other_device_data
1847 } = alice_request.state()
1848 );
1849
1850 assert_eq!(bob_device_data, other_device_data);
1851
1852 assert_let!(
1853 VerificationRequestState::Transitioned {
1854 verification: Verification::SasV1(_),
1855 other_device_data
1856 } = bob_request.state()
1857 );
1858
1859 assert_eq!(alice_device_data, other_device_data);
1860
1861 assert!(!bob_sas.is_cancelled());
1862 assert!(!alice_sas.is_cancelled());
1863 assert!(alice_sas.started_from_request());
1864 assert!(bob_sas.started_from_request());
1865 }
1866
1867 #[async_test]
1868 #[cfg(feature = "qrcode")]
1869 async fn test_can_scan_another_qr_after_creating_mine() {
1870 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1871
1872 let alice_device_data = DeviceData::from_account(&alice);
1873 let bob_device_data = DeviceData::from_account(&bob);
1874
1875 let bob_request = build_test_request(
1877 &bob_store,
1878 alice_id(),
1879 Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]),
1880 );
1881 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1882 do_accept_request(
1883 &alice_request,
1884 alice_device_data.clone(),
1885 &bob_request,
1886 Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]),
1887 );
1888
1889 let alice_verification = alice_request.generate_qr_code().await.unwrap();
1892 let bob_verification = bob_request.generate_qr_code().await.unwrap();
1893
1894 assert_let!(
1895 VerificationRequestState::Transitioned {
1896 verification: Verification::QrV1(_),
1897 other_device_data
1898 } = alice_request.state()
1899 );
1900
1901 assert_eq!(bob_device_data, other_device_data);
1902
1903 assert_let!(
1904 VerificationRequestState::Transitioned {
1905 verification: Verification::QrV1(_),
1906 other_device_data
1907 } = bob_request.state()
1908 );
1909
1910 assert_eq!(alice_device_data, other_device_data);
1911
1912 assert!(alice_verification.is_some());
1913 assert!(bob_verification.is_some());
1914
1915 let bob_qr_code = bob_verification.unwrap().to_bytes().unwrap();
1917 let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap();
1918 let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap();
1919
1920 assert_let!(
1921 VerificationRequestState::Transitioned {
1922 verification: Verification::QrV1(alice_verification),
1923 other_device_data
1924 } = alice_request.state()
1925 );
1926
1927 assert_eq!(bob_device_data, other_device_data);
1928
1929 assert!(!alice_verification.is_cancelled());
1932 assert!(alice_verification.reciprocated());
1933 }
1934
1935 #[async_test]
1936 #[cfg(feature = "qrcode")]
1937 async fn test_can_start_sas_after_generating_qr_code() {
1938 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1939
1940 let alice_device_data = DeviceData::from_account(&alice);
1941 let bob_device_data = DeviceData::from_account(&bob);
1942
1943 let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods()));
1945 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1946 do_accept_request(
1947 &alice_request,
1948 alice_device_data.clone(),
1949 &bob_request,
1950 Some(all_methods()),
1951 );
1952
1953 let alice_verification = alice_request.generate_qr_code().await.unwrap();
1956 let bob_verification = bob_request.generate_qr_code().await.unwrap();
1957
1958 assert_let!(
1959 VerificationRequestState::Transitioned {
1960 verification: Verification::QrV1(_),
1961 other_device_data
1962 } = alice_request.state()
1963 );
1964
1965 assert_eq!(bob_device_data, other_device_data);
1966
1967 assert!(alice_verification.is_some());
1968 assert!(bob_verification.is_some());
1969
1970 let (sas, request) = alice_request.start_sas().await.unwrap().unwrap();
1973 assert_let!(
1974 VerificationRequestState::Transitioned {
1975 verification: Verification::SasV1(_),
1976 other_device_data
1977 } = alice_request.state()
1978 );
1979
1980 assert_eq!(bob_device_data, other_device_data);
1981 assert!(!sas.is_cancelled());
1982
1983 let content: OutgoingContent = request.try_into().unwrap();
1985 let content = StartContent::try_from(&content).unwrap();
1986 bob_request.receive_start(alice_id(), &content).await.unwrap();
1987
1988 assert_let!(
1990 VerificationRequestState::Transitioned {
1991 verification: Verification::SasV1(bob_sas),
1992 other_device_data
1993 } = bob_request.state()
1994 );
1995
1996 assert_eq!(alice_device_data, other_device_data);
1997
1998 assert!(!bob_sas.is_cancelled());
2000 }
2001
2002 #[async_test]
2003 #[cfg(feature = "qrcode")]
2004 async fn test_start_sas_after_scan_cancels_request() {
2005 let (alice, alice_store, bob, bob_store) = setup_stores().await;
2006
2007 let alice_device_data = DeviceData::from_account(&alice);
2008 let bob_device_data = DeviceData::from_account(&bob);
2009
2010 let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods()));
2012 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
2013 do_accept_request(
2014 &alice_request,
2015 alice_device_data.clone(),
2016 &bob_request,
2017 Some(all_methods()),
2018 );
2019
2020 let bob_verification = bob_request.generate_qr_code().await.unwrap().unwrap();
2022 assert_let!(
2023 VerificationRequestState::Transitioned {
2024 verification: Verification::QrV1(_),
2025 other_device_data
2026 } = bob_request.state()
2027 );
2028
2029 assert_eq!(alice_device_data, other_device_data);
2030
2031 let bob_qr_code = bob_verification.to_bytes().unwrap();
2033 let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap();
2034 let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap();
2035
2036 assert_let!(
2037 VerificationRequestState::Transitioned {
2038 verification: Verification::QrV1(alice_qr),
2039 other_device_data
2040 } = alice_request.state()
2041 );
2042
2043 assert_eq!(bob_device_data, other_device_data);
2044 assert!(alice_qr.reciprocated());
2045
2046 let (_, request) = bob_request.start_sas().await.unwrap().unwrap();
2048 assert_let!(
2049 VerificationRequestState::Transitioned {
2050 verification: Verification::SasV1(_),
2051 other_device_data
2052 } = bob_request.state()
2053 );
2054
2055 assert_eq!(alice_device_data, other_device_data);
2056
2057 let content: OutgoingContent = request.try_into().unwrap();
2059 let content = StartContent::try_from(&content).unwrap();
2060 alice_request.receive_start(bob_id(), &content).await.unwrap();
2061
2062 assert!(alice_qr.is_cancelled());
2064
2065 assert_let!(
2067 VerificationRequestState::Transitioned {
2068 verification: Verification::SasV1(alice_sas),
2069 other_device_data
2070 } = alice_request.state()
2071 );
2072
2073 assert_eq!(bob_device_data, other_device_data);
2074 assert!(alice_sas.is_cancelled());
2075 }
2076
2077 fn build_test_request(
2087 verification_store: &VerificationStore,
2088 other_user_id: &UserId,
2089 methods: Option<Vec<VerificationMethod>>,
2090 ) -> VerificationRequest {
2091 VerificationRequest::new(
2092 VerificationCache::new(),
2093 verification_store.clone(),
2094 FlowId::ToDevice("TEST_FLOW_ID".into()),
2095 other_user_id,
2096 vec![],
2097 methods,
2098 )
2099 }
2100
2101 async fn build_incoming_verification_request(
2108 verification_store: &VerificationStore,
2109 outgoing_request: &VerificationRequest,
2110 ) -> VerificationRequest {
2111 let request = outgoing_request.request_to_device();
2112 let content: OutgoingContent = request.try_into().unwrap();
2113 let content = RequestContent::try_from(&content).unwrap();
2114
2115 let device_data = verification_store
2116 .get_device(outgoing_request.own_user_id(), content.from_device())
2117 .await
2118 .unwrap()
2119 .expect("Missing device data");
2120
2121 VerificationRequest::from_request(
2122 VerificationCache::new(),
2123 verification_store.clone(),
2124 outgoing_request.own_user_id(),
2125 outgoing_request.flow_id().clone(),
2126 &content,
2127 device_data,
2128 )
2129 }
2130
2131 fn do_accept_request(
2142 accepting_request: &VerificationRequest,
2143 accepting_device_data: DeviceData,
2144 initiating_request: &VerificationRequest,
2145 methods: Option<Vec<VerificationMethod>>,
2146 ) {
2147 let request = match methods {
2148 Some(methods) => accepting_request.accept_with_methods(methods),
2149 None => accepting_request.accept(),
2150 };
2151 let content: OutgoingContent = request.unwrap().try_into().unwrap();
2152 let content = ReadyContent::try_from(&content).unwrap();
2153 initiating_request.receive_ready(
2154 accepting_request.own_user_id(),
2155 &content,
2156 accepting_device_data,
2157 );
2158
2159 assert!(initiating_request.is_ready());
2160 assert!(accepting_request.is_ready());
2161 }
2162
2163 #[cfg(feature = "qrcode")]
2166 fn all_methods() -> Vec<VerificationMethod> {
2167 vec![
2168 VerificationMethod::SasV1,
2169 VerificationMethod::QrCodeScanV1,
2170 VerificationMethod::QrCodeShowV1,
2171 VerificationMethod::ReciprocateV1,
2172 ]
2173 }
2174}