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 {
102 verification: Verification,
105
106 other_device_data: DeviceData,
109 },
110 Done,
112 Cancelled(CancelInfo),
114}
115
116impl From<&InnerRequest> for VerificationRequestState {
117 fn from(value: &InnerRequest) -> Self {
118 match value {
119 InnerRequest::Created(s) => {
120 Self::Created { our_methods: s.state.our_methods.to_owned() }
121 }
122 InnerRequest::Requested(s) => Self::Requested {
123 their_methods: s.state.their_methods.to_owned(),
124 other_device_data: s.state.other_device_data.to_owned(),
125 },
126 InnerRequest::Ready(s) => Self::Ready {
127 their_methods: s.state.their_methods.to_owned(),
128 our_methods: s.state.our_methods.to_owned(),
129 other_device_data: s.state.other_device_data.to_owned(),
130 },
131 InnerRequest::Transitioned(s) => Self::Transitioned {
132 verification: s.state.verification.to_owned(),
133 other_device_data: s.state.other_device_data.to_owned(),
134 },
135 InnerRequest::Passive(_) => {
136 Self::Cancelled(Cancelled::new(true, CancelCode::Accepted).into())
137 }
138 InnerRequest::Done(_) => Self::Done,
139 InnerRequest::Cancelled(s) => Self::Cancelled(s.state.to_owned().into()),
140 }
141 }
142}
143
144#[derive(Clone, Debug)]
152pub struct VerificationRequest {
153 verification_cache: VerificationCache,
154 account: StaticAccountData,
155 flow_id: Arc<FlowId>,
156 other_user_id: OwnedUserId,
157 inner: SharedObservable<InnerRequest>,
158 creation_time: Arc<Instant>,
159 we_started: bool,
160 recipient_devices: Arc<Vec<OwnedDeviceId>>,
161}
162
163#[derive(Debug, Clone)]
172pub(crate) struct RequestHandle {
173 inner: WeakObservable<InnerRequest>,
174}
175
176impl RequestHandle {
177 pub fn cancel_with_code(&self, cancel_code: &CancelCode) {
178 if let Some(observable) = self.inner.upgrade() {
179 let mut guard = observable.write();
180
181 if let Some(updated) = guard.cancel(true, cancel_code) {
182 ObservableWriteGuard::set(&mut guard, updated);
183 }
184 }
185 }
186}
187
188impl From<SharedObservable<InnerRequest>> for RequestHandle {
189 fn from(inner: SharedObservable<InnerRequest>) -> Self {
190 let inner = inner.downgrade();
191
192 Self { inner }
193 }
194}
195
196impl VerificationRequest {
197 pub(crate) fn new(
198 cache: VerificationCache,
199 store: VerificationStore,
200 flow_id: FlowId,
201 other_user: &UserId,
202 recipient_devices: Vec<OwnedDeviceId>,
203 methods: Option<Vec<VerificationMethod>>,
204 ) -> Self {
205 let account = store.account.clone();
206 let inner = SharedObservable::new(InnerRequest::Created(RequestState::new(
207 cache.clone(),
208 store,
209 other_user,
210 &flow_id,
211 methods,
212 )));
213
214 Self {
215 account,
216 verification_cache: cache,
217 flow_id: flow_id.into(),
218 inner,
219 other_user_id: other_user.into(),
220 creation_time: Instant::now().into(),
221 we_started: true,
222 recipient_devices: recipient_devices.into(),
223 }
224 }
225
226 pub(crate) fn request_to_device(&self) -> ToDeviceRequest {
231 let inner = self.inner.read();
232
233 let methods = if let InnerRequest::Created(c) = &*inner {
234 c.state.our_methods.clone()
235 } else {
236 SUPPORTED_METHODS.to_vec()
237 };
238
239 let content = ToDeviceKeyVerificationRequestEventContent::new(
240 self.account.device_id.clone(),
241 self.flow_id().as_str().into(),
242 methods,
243 MilliSecondsSinceUnixEpoch::now(),
244 );
245
246 ToDeviceRequest::for_recipients(
247 self.other_user(),
248 self.recipient_devices.to_vec(),
249 &AnyToDeviceEventContent::KeyVerificationRequest(content),
250 TransactionId::new(),
251 )
252 }
253
254 pub fn request(
259 own_user_id: &UserId,
260 own_device_id: &DeviceId,
261 other_user_id: &UserId,
262 methods: Option<Vec<VerificationMethod>>,
263 ) -> KeyVerificationRequestEventContent {
264 KeyVerificationRequestEventContent::new(
265 format!(
266 "{own_user_id} is requesting to verify your key, but your client does not \
267 support in-chat key verification. You will need to use legacy \
268 key verification to verify keys."
269 ),
270 methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec()),
271 own_device_id.into(),
272 other_user_id.to_owned(),
273 )
274 }
275
276 pub fn own_user_id(&self) -> &UserId {
278 &self.account.user_id
279 }
280
281 pub fn other_user(&self) -> &UserId {
284 &self.other_user_id
285 }
286
287 pub fn other_device_id(&self) -> Option<OwnedDeviceId> {
289 match &*self.inner.read() {
290 InnerRequest::Requested(r) => Some(r.state.other_device_data.device_id().to_owned()),
291 InnerRequest::Ready(r) => Some(r.state.other_device_data.device_id().to_owned()),
292 InnerRequest::Transitioned(r) => {
293 Some(r.state.ready.other_device_data.device_id().to_owned())
294 }
295 InnerRequest::Created(_)
296 | InnerRequest::Passive(_)
297 | InnerRequest::Done(_)
298 | InnerRequest::Cancelled(_) => None,
299 }
300 }
301
302 pub fn room_id(&self) -> Option<&RoomId> {
304 match self.flow_id.as_ref() {
305 FlowId::ToDevice(_) => None,
306 FlowId::InRoom(r, _) => Some(r),
307 }
308 }
309
310 pub fn cancel_info(&self) -> Option<CancelInfo> {
313 as_variant!(&*self.inner.read(), InnerRequest::Cancelled(c) => {
314 c.state.clone().into()
315 })
316 }
317
318 pub fn is_passive(&self) -> bool {
320 matches!(*self.inner.read(), InnerRequest::Passive(_))
321 }
322
323 pub fn is_ready(&self) -> bool {
325 matches!(*self.inner.read(), InnerRequest::Ready(_))
326 }
327
328 pub fn timed_out(&self) -> bool {
330 self.creation_time.elapsed() > VERIFICATION_TIMEOUT
331 }
332
333 pub fn time_remaining(&self) -> Duration {
336 self.creation_time
337 .add(VERIFICATION_TIMEOUT)
338 .checked_duration_since(Instant::now())
339 .unwrap_or(Duration::from_secs(0))
340 }
341
342 pub fn their_supported_methods(&self) -> Option<Vec<VerificationMethod>> {
347 match &*self.inner.read() {
348 InnerRequest::Requested(r) => Some(r.state.their_methods.clone()),
349 InnerRequest::Ready(r) => Some(r.state.their_methods.clone()),
350 InnerRequest::Transitioned(r) => Some(r.state.ready.their_methods.clone()),
351 InnerRequest::Created(_)
352 | InnerRequest::Passive(_)
353 | InnerRequest::Done(_)
354 | InnerRequest::Cancelled(_) => None,
355 }
356 }
357
358 pub fn our_supported_methods(&self) -> Option<Vec<VerificationMethod>> {
363 match &*self.inner.read() {
364 InnerRequest::Created(r) => Some(r.state.our_methods.clone()),
365 InnerRequest::Ready(r) => Some(r.state.our_methods.clone()),
366 InnerRequest::Transitioned(r) => Some(r.state.ready.our_methods.clone()),
367 InnerRequest::Requested(_)
368 | InnerRequest::Passive(_)
369 | InnerRequest::Done(_)
370 | InnerRequest::Cancelled(_) => None,
371 }
372 }
373
374 pub fn flow_id(&self) -> &FlowId {
376 &self.flow_id
377 }
378
379 pub fn is_self_verification(&self) -> bool {
381 self.account.user_id == self.other_user()
382 }
383
384 pub fn we_started(&self) -> bool {
386 self.we_started
387 }
388
389 pub fn is_done(&self) -> bool {
391 matches!(*self.inner.read(), InnerRequest::Done(_))
392 }
393
394 pub fn is_cancelled(&self) -> bool {
397 matches!(*self.inner.read(), InnerRequest::Cancelled(_))
398 }
399
400 #[cfg(feature = "qrcode")]
403 pub async fn generate_qr_code(&self) -> Result<Option<QrVerification>, CryptoStoreError> {
404 let inner = self.inner.get();
405
406 let ret = if let Some((state, verification)) =
407 inner.generate_qr_code(self.we_started, self.inner.clone().into()).await?
408 {
409 let mut inner = self.inner.write();
410 ObservableWriteGuard::set(&mut inner, InnerRequest::Transitioned(state));
411
412 Some(verification)
413 } else {
414 None
415 };
416
417 Ok(ret)
418 }
419
420 #[cfg(feature = "qrcode")]
428 pub async fn scan_qr_code(
429 &self,
430 data: QrVerificationData,
431 ) -> Result<Option<QrVerification>, ScanError> {
432 let inner = self.inner.read().to_owned();
433
434 let (new_state, qr_verification) = match inner {
435 InnerRequest::Ready(r) => {
436 scan_qr_code(data, &r, &r.state, self.we_started, self.inner.to_owned().into())
437 .await?
438 }
439 InnerRequest::Transitioned(r) => {
440 scan_qr_code(
441 data,
442 &r,
443 &r.state.ready,
444 self.we_started,
445 self.inner.to_owned().into(),
446 )
447 .await?
448 }
449 _ => return Ok(None),
450 };
451
452 if self
456 .verification_cache
457 .get_qr(qr_verification.other_user_id(), qr_verification.flow_id().as_str())
458 .is_some()
459 {
460 self.verification_cache.replace_qr(qr_verification.clone());
461 } else {
462 self.verification_cache.insert_qr(qr_verification.clone());
463 }
464
465 let mut guard = self.inner.write();
466 ObservableWriteGuard::set(&mut guard, InnerRequest::Transitioned(new_state));
467
468 Ok(Some(qr_verification))
469 }
470
471 pub(crate) fn from_request(
472 cache: VerificationCache,
473 store: VerificationStore,
474 sender: &UserId,
475 flow_id: FlowId,
476 content: &RequestContent<'_>,
477 device_data: DeviceData,
478 ) -> Self {
479 let account = store.account.clone();
480
481 Self {
482 verification_cache: cache.clone(),
483 inner: SharedObservable::new(InnerRequest::Requested(
484 RequestState::from_request_event(
485 cache,
486 store,
487 sender,
488 &flow_id,
489 content,
490 device_data,
491 ),
492 )),
493 account,
494 other_user_id: sender.into(),
495 flow_id: flow_id.into(),
496 we_started: false,
497 creation_time: Instant::now().into(),
498 recipient_devices: vec![].into(),
499 }
500 }
501
502 pub fn accept_with_methods(
509 &self,
510 methods: Vec<VerificationMethod>,
511 ) -> Option<OutgoingVerificationRequest> {
512 let mut guard = self.inner.write();
513 let (updated, content) = guard.accept(methods)?;
514
515 ObservableWriteGuard::set(&mut guard, updated);
516
517 let request = match content {
518 OutgoingContent::ToDevice(content) => ToDeviceRequest::with_id(
519 self.other_user(),
520 guard.other_device_id(),
521 &content,
522 TransactionId::new(),
523 )
524 .into(),
525 OutgoingContent::Room(room_id, content) => {
526 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
527 }
528 };
529
530 Some(request)
531 }
532
533 pub fn accept(&self) -> Option<OutgoingVerificationRequest> {
546 self.accept_with_methods(SUPPORTED_METHODS.to_vec())
547 }
548
549 pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
551 self.cancel_with_code(CancelCode::User)
552 }
553
554 fn cancel_with_code(&self, cancel_code: CancelCode) -> Option<OutgoingVerificationRequest> {
555 let mut guard = self.inner.write();
556
557 let send_to_everyone = self.we_started() && matches!(*guard, InnerRequest::Created(_));
558 let other_device = guard.other_device_id();
559
560 if let Some(updated) = guard.cancel(true, &cancel_code) {
561 ObservableWriteGuard::set(&mut guard, updated);
562 }
563
564 let content = as_variant!(&*guard, InnerRequest::Cancelled(c) => {
565 c.state.as_content(self.flow_id())
566 });
567
568 let request = content.map(|c| match c {
569 OutgoingContent::ToDevice(content) => {
570 if send_to_everyone {
571 ToDeviceRequest::for_recipients(
572 self.other_user(),
573 self.recipient_devices.to_vec(),
574 &content,
575 TransactionId::new(),
576 )
577 .into()
578 } else {
579 ToDeviceRequest::with_id(
580 self.other_user(),
581 other_device,
582 &content,
583 TransactionId::new(),
584 )
585 .into()
586 }
587 }
588 OutgoingContent::Room(room_id, content) => {
589 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
590 }
591 });
592
593 drop(guard);
594
595 if let Some(verification) =
596 self.verification_cache.get(self.other_user(), self.flow_id().as_str())
597 {
598 match verification {
599 Verification::SasV1(s) => s.cancel_with_code(cancel_code),
600 #[cfg(feature = "qrcode")]
601 Verification::QrV1(q) => q.cancel_with_code(cancel_code),
602 };
603 }
604
605 request
606 }
607
608 pub(crate) fn cancel_if_timed_out(&self) -> Option<OutgoingVerificationRequest> {
609 if self.is_cancelled() || self.is_done() {
610 None
611 } else if self.timed_out() {
612 let request = self.cancel_with_code(CancelCode::Timeout);
613
614 if self.is_passive() {
615 None
616 } else {
617 trace!(
618 other_user = self.other_user().as_str(),
619 flow_id = self.flow_id().as_str(),
620 "Timing a verification request out"
621 );
622 request
623 }
624 } else {
625 None
626 }
627 }
628
629 pub(crate) fn cancel_for_other_devices(
650 &self,
651 code: CancelCode,
652 filter_device: Option<&DeviceId>,
653 ) -> Option<ToDeviceRequest> {
654 let cancelled = Cancelled::new(true, code);
655 let cancel_content = cancelled.as_content(self.flow_id());
656
657 let OutgoingContent::ToDevice(c) = cancel_content else { return None };
658 let recip_devices: Vec<OwnedDeviceId> = self
659 .recipient_devices
660 .iter()
661 .filter(|&d| filter_device.is_none_or(|device| **d != *device))
662 .cloned()
663 .collect();
664
665 if recip_devices.is_empty() && filter_device.is_some() {
666 return None;
671 }
672
673 let recipient = self.other_user();
674 Some(ToDeviceRequest::for_recipients(recipient, recip_devices, &c, TransactionId::new()))
675 }
676
677 pub(crate) fn receive_ready(
678 &self,
679 sender: &UserId,
680 content: &ReadyContent<'_>,
681 from_device_data: DeviceData,
682 ) {
683 let mut guard = self.inner.write();
684
685 match &*guard {
686 InnerRequest::Created(s) => {
687 let new_value =
688 InnerRequest::Ready(s.clone().into_ready(sender, content, from_device_data));
689 ObservableWriteGuard::set(&mut guard, new_value);
690
691 if let Some(request) =
692 self.cancel_for_other_devices(CancelCode::Accepted, Some(content.from_device()))
693 {
694 self.verification_cache.add_verification_request(request.into());
695 }
696 }
697 InnerRequest::Requested(s) => {
698 if sender == self.own_user_id() && content.from_device() != self.account.device_id {
699 let new_value = InnerRequest::Passive(s.clone().into_passive(content));
700 ObservableWriteGuard::set(&mut guard, new_value);
701 }
702 }
703 InnerRequest::Ready(_)
704 | InnerRequest::Transitioned(_)
705 | InnerRequest::Passive(_)
706 | InnerRequest::Done(_)
707 | InnerRequest::Cancelled(_) => {}
708 }
709 }
710
711 pub(crate) async fn receive_start(
712 &self,
713 sender: &UserId,
714 content: &StartContent<'_>,
715 ) -> Result<(), CryptoStoreError> {
716 let inner = self.inner.get();
717
718 match &inner {
719 InnerRequest::Created(_)
720 | InnerRequest::Requested(_)
721 | InnerRequest::Passive(_)
722 | InnerRequest::Done(_)
723 | InnerRequest::Cancelled(_) => {
724 warn!(
725 ?sender,
726 device_id = content.from_device().as_str(),
727 "Received a key verification start event but we're not yet in the ready state"
728 );
729 Ok(())
730 }
731 InnerRequest::Ready(s) => {
732 let s = s.clone();
733
734 if let Some(new_state) = s
735 .receive_start(sender, content, self.we_started, self.inner.clone().into())
736 .await?
737 {
738 let mut inner = self.inner.write();
739 ObservableWriteGuard::set(&mut inner, InnerRequest::Transitioned(new_state));
740 }
741
742 Ok(())
743 }
744 InnerRequest::Transitioned(s) => {
745 let s = s.clone();
752
753 if let Some(new_state) = s
754 .receive_start(sender, content, self.we_started, self.inner.clone().into())
755 .await?
756 {
757 let mut inner = self.inner.write();
758 ObservableWriteGuard::set(&mut inner, InnerRequest::Transitioned(new_state));
759 }
760
761 Ok(())
762 }
763 }
764 }
765
766 pub(crate) fn receive_done(&self, sender: &UserId, content: &DoneContent<'_>) {
767 if sender == self.other_user() {
768 trace!(
769 other_user = ?self.other_user(),
770 flow_id = self.flow_id().as_str(),
771 "Marking a verification request as done"
772 );
773
774 let mut guard = self.inner.write();
775 if let Some(updated) = guard.receive_done(content) {
776 ObservableWriteGuard::set(&mut guard, updated);
777 }
778 }
779 }
780
781 pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) {
782 if sender != self.other_user() {
783 return;
784 }
785
786 trace!(
787 ?sender,
788 code = content.cancel_code().as_str(),
789 "Cancelling a verification request, other user has cancelled"
790 );
791 let mut guard = self.inner.write();
792 if let Some(updated) = guard.cancel(false, content.cancel_code()) {
793 ObservableWriteGuard::set(&mut guard, updated);
794 }
795
796 if self.we_started()
797 && let Some(request) =
798 self.cancel_for_other_devices(content.cancel_code().to_owned(), None)
799 {
800 self.verification_cache.add_verification_request(request.into());
801 }
802 }
803
804 fn start_sas_helper(
805 &self,
806 new_state: RequestState<Transitioned>,
807 sas: Sas,
808 content: OutgoingContent,
809 other_device_id: DeviceIdOrAllDevices,
810 ) -> Option<(Sas, OutgoingVerificationRequest)> {
811 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;
1385 if !matches!(
1386 (
1387 sender.cmp(own_user_id),
1388 other_device_data.device_id().cmp(own_device_id)
1389 ),
1390 (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater)
1391 ) {
1392 info!(
1393 "Started a new SAS verification, replacing an already started one."
1394 );
1395 request_state.verification_cache.replace_sas(new.to_owned());
1396 Ok(Some(state.to_transitioned(request_state, new.into())))
1397 } else {
1398 info!(
1399 "Ignored incoming SAS verification from lexicographically larger user/device ID."
1400 );
1401 Ok(None)
1402 }
1403 }
1404 #[cfg(feature = "qrcode")]
1405 Some(Verification::QrV1(old)) => {
1406 if let QrVerificationState::Started = old.state() {
1409 info!("Transitioned from QR display to SAS");
1411 request_state.verification_cache.replace_sas(new.to_owned());
1412 Ok(Some(state.to_transitioned(request_state, new.into())))
1413 } else {
1414 warn!(qr_state = ?old.state(), "Invalid transition from QR to SAS");
1419 request_state.verification_cache.insert_sas(new.to_owned());
1420 Ok(Some(state.to_transitioned(request_state, new.into())))
1421 }
1422 }
1423 None => {
1424 info!("Started a new SAS verification.");
1425 request_state.verification_cache.insert_sas(new.to_owned());
1426 Ok(Some(state.to_transitioned(request_state, new.into())))
1427 }
1428 }
1429 }
1430 Err(c) => {
1431 warn!(
1432 user_id = ?other_device_data.user_id(),
1433 device_id = ?other_device_data.device_id(),
1434 content = ?c,
1435 "Can't start key verification, canceling.",
1436 );
1437 request_state.verification_cache.queue_up_content(
1438 other_device_data.user_id(),
1439 other_device_data.device_id(),
1440 c,
1441 None,
1442 );
1443
1444 Ok(None)
1445 }
1446 }
1447 }
1448 #[cfg(feature = "qrcode")]
1449 StartMethod::ReciprocateV1(_) => {
1450 if let Some(qr_verification) =
1451 request_state.verification_cache.get_qr(sender, content.flow_id())
1452 {
1453 if let Some(request) = qr_verification.receive_reciprocation(content) {
1454 request_state.verification_cache.add_request(request.into())
1455 }
1456 debug!(
1457 sender = ?identities.device_being_verified.user_id(),
1458 device_id = ?identities.device_being_verified.device_id(),
1459 verification = ?qr_verification,
1460 "Received a QR code reciprocation"
1461 );
1462
1463 Ok(None)
1464 } else {
1465 warn!("Received a QR code reciprocation for an unknown flow");
1466 Ok(None)
1467 }
1468 }
1469 m => {
1470 warn!(method = ?m, "Received a key verification start event with an unsupported method");
1471 Ok(None)
1472 }
1473 }
1474}
1475
1476async fn start_sas<T: Clone>(
1477 request_state: &RequestState<T>,
1478 state: &Ready,
1479 we_started: bool,
1480 request_handle: RequestHandle,
1481) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1482 if !state.their_methods.contains(&VerificationMethod::SasV1) {
1483 return Ok(None);
1484 }
1485
1486 let identities = request_state.store.get_identities(state.other_device_data.clone()).await?;
1487
1488 let (state, sas, content) = match request_state.flow_id.as_ref() {
1489 FlowId::ToDevice(t) => {
1490 let (sas, content) =
1491 Sas::start(identities, t.to_owned(), we_started, Some(request_handle), None);
1492
1493 let state = Transitioned {
1494 ready: state.to_owned(),
1495 verification: sas.to_owned().into(),
1496 other_device_data: state.other_device_data.to_owned(),
1497 };
1498
1499 (state, sas, content)
1500 }
1501 FlowId::InRoom(r, e) => {
1502 let (sas, content) = Sas::start_in_room(
1503 e.to_owned(),
1504 r.to_owned(),
1505 identities,
1506 we_started,
1507 request_handle,
1508 );
1509 let state = Transitioned {
1510 ready: state.to_owned(),
1511 verification: sas.to_owned().into(),
1512 other_device_data: state.other_device_data.to_owned(),
1513 };
1514 (state, sas, content)
1515 }
1516 };
1517
1518 let state = RequestState {
1519 verification_cache: request_state.verification_cache.to_owned(),
1520 store: request_state.store.to_owned(),
1521 flow_id: request_state.flow_id.to_owned(),
1522 other_user_id: request_state.other_user_id.to_owned(),
1523 state,
1524 };
1525
1526 Ok(Some((state, sas, content)))
1527}
1528
1529impl RequestState<Ready> {
1530 #[cfg(feature = "qrcode")]
1531 async fn generate_qr_code(
1532 &self,
1533 we_started: bool,
1534 request_handle: RequestHandle,
1535 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1536 generate_qr_code(self, &self.state, we_started, request_handle).await
1537 }
1538
1539 async fn receive_start(
1540 &self,
1541 sender: &UserId,
1542 content: &StartContent<'_>,
1543 we_started: bool,
1544 request_handle: RequestHandle,
1545 ) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1546 receive_start(sender, content, we_started, request_handle, self, &self.state).await
1547 }
1548
1549 async fn start_sas(
1550 &self,
1551 we_started: bool,
1552 request_handle: RequestHandle,
1553 ) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1554 start_sas(self, &self.state, we_started, request_handle).await
1555 }
1556}
1557
1558impl Ready {
1559 fn to_transitioned<T: Clone>(
1560 &self,
1561 request_state: &RequestState<T>,
1562 verification: Verification,
1563 ) -> RequestState<Transitioned> {
1564 RequestState {
1565 verification_cache: request_state.verification_cache.to_owned(),
1566 store: request_state.store.to_owned(),
1567 flow_id: request_state.flow_id.to_owned(),
1568 other_user_id: request_state.other_user_id.to_owned(),
1569 state: Transitioned {
1570 ready: self.clone(),
1571 verification,
1572 other_device_data: self.other_device_data.clone(),
1573 },
1574 }
1575 }
1576}
1577
1578#[derive(Clone, Debug)]
1579struct Transitioned {
1580 ready: Ready,
1581 verification: Verification,
1582 other_device_data: DeviceData,
1583}
1584
1585impl RequestState<Transitioned> {
1586 #[cfg(feature = "qrcode")]
1587 async fn generate_qr_code(
1588 &self,
1589 we_started: bool,
1590 request_handle: RequestHandle,
1591 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1592 generate_qr_code(self, &self.state.ready, we_started, request_handle).await
1593 }
1594
1595 async fn receive_start(
1596 &self,
1597 sender: &UserId,
1598 content: &StartContent<'_>,
1599 we_started: bool,
1600 request_handle: RequestHandle,
1601 ) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1602 receive_start(sender, content, we_started, request_handle, self, &self.state.ready).await
1603 }
1604
1605 async fn start_sas(
1606 &self,
1607 we_started: bool,
1608 request_handle: RequestHandle,
1609 ) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1610 start_sas(self, &self.state.ready, we_started, request_handle).await
1611 }
1612}
1613
1614#[derive(Clone, Debug)]
1615struct Passive {
1616 #[allow(dead_code)]
1618 pub other_device_id: OwnedDeviceId,
1619}
1620
1621#[derive(Clone, Debug)]
1622struct Done {}
1623
1624#[cfg(test)]
1625mod tests {
1626
1627 use std::time::Duration;
1628
1629 use assert_matches::assert_matches;
1630 use assert_matches2::assert_let;
1631 #[cfg(feature = "qrcode")]
1632 use matrix_sdk_qrcode::QrVerificationData;
1633 use matrix_sdk_test::async_test;
1634 use ruma::{
1635 UserId, event_id, events::key::verification::VerificationMethod, room_id,
1636 to_device::DeviceIdOrAllDevices,
1637 };
1638
1639 use super::VerificationRequest;
1640 use crate::{
1641 DeviceData, VerificationRequestState,
1642 types::requests::OutgoingVerificationRequest,
1643 verification::{
1644 FlowId, Verification, VerificationStore,
1645 cache::VerificationCache,
1646 event_enums::{
1647 CancelContent, OutgoingContent, ReadyContent, RequestContent, StartContent,
1648 },
1649 tests::{alice_id, bob_id, setup_stores},
1650 },
1651 };
1652
1653 #[async_test]
1654 async fn test_request_accepting() {
1655 let event_id = event_id!("$1234localhost").to_owned();
1656 let room_id = room_id!("!test:localhost").to_owned();
1657
1658 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1659
1660 let alice_device_data = DeviceData::from_account(&alice);
1661 let bob_device_data = DeviceData::from_account(&bob);
1662
1663 let content = VerificationRequest::request(
1664 &bob_store.account.user_id,
1665 &bob_store.account.device_id,
1666 alice_id(),
1667 None,
1668 );
1669
1670 let flow_id = FlowId::InRoom(room_id, event_id);
1671
1672 let bob_request = VerificationRequest::new(
1673 VerificationCache::new(),
1674 bob_store,
1675 flow_id.clone(),
1676 alice_id(),
1677 vec![],
1678 None,
1679 );
1680
1681 assert_matches!(bob_request.state(), VerificationRequestState::Created { .. });
1682 assert!(bob_request.time_remaining() <= Duration::from_secs(600)); assert!(bob_request.time_remaining() > Duration::from_secs(540)); #[allow(clippy::needless_borrow)]
1686 let alice_request = VerificationRequest::from_request(
1687 VerificationCache::new(),
1688 alice_store,
1689 bob_id(),
1690 flow_id,
1691 &(&content).into(),
1692 bob_device_data,
1693 );
1694
1695 assert_matches!(alice_request.state(), VerificationRequestState::Requested { .. });
1696
1697 let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap();
1698 let content = ReadyContent::try_from(&content).unwrap();
1699
1700 bob_request.receive_ready(alice_id(), &content, alice_device_data);
1701
1702 assert_matches!(bob_request.state(), VerificationRequestState::Ready { .. });
1703 assert_matches!(alice_request.state(), VerificationRequestState::Ready { .. });
1704 assert!(bob_request.is_ready());
1705 assert!(alice_request.is_ready());
1706 }
1707
1708 #[async_test]
1709 async fn test_request_refusal_to_device() {
1710 let (_alice, alice_store, bob, bob_store) = setup_stores().await;
1713 let bob_device = DeviceData::from_account(&bob);
1714
1715 let bob_request = build_test_request(&bob_store, alice_id(), None);
1717 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1718
1719 let outgoing_request = alice_request.cancel().unwrap();
1720
1721 {
1723 assert_let!(
1724 OutgoingVerificationRequest::ToDevice(to_device_request) = &outgoing_request
1725 );
1726
1727 assert_eq!(to_device_request.messages.len(), 1);
1728 let device_ids: Vec<&DeviceIdOrAllDevices> =
1729 to_device_request.messages.values().next().unwrap().keys().collect();
1730 assert_eq!(device_ids.len(), 1);
1731
1732 assert_let!(DeviceIdOrAllDevices::DeviceId(device_id) = &device_ids[0]);
1733 assert_eq!(device_id, bob_device.device_id());
1734 }
1735
1736 let content = OutgoingContent::try_from(outgoing_request).unwrap();
1737 let content = CancelContent::try_from(&content).unwrap();
1738
1739 bob_request.receive_cancel(alice_id(), &content);
1740
1741 assert_matches!(bob_request.state(), VerificationRequestState::Cancelled { .. });
1742 assert_matches!(alice_request.state(), VerificationRequestState::Cancelled { .. });
1743 }
1744
1745 #[async_test]
1746 async fn test_requesting_until_sas() {
1747 let event_id = event_id!("$1234localhost");
1748 let room_id = room_id!("!test:localhost");
1749
1750 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1751
1752 let alice_device_data = DeviceData::from_account(&alice);
1753 let bob_device_data = DeviceData::from_account(&bob);
1754
1755 let content = VerificationRequest::request(
1756 &bob_store.account.user_id,
1757 &bob_store.account.device_id,
1758 alice_id(),
1759 None,
1760 );
1761
1762 let flow_id = FlowId::from((room_id, event_id));
1763
1764 let bob_request = VerificationRequest::new(
1765 VerificationCache::new(),
1766 bob_store,
1767 flow_id.clone(),
1768 alice_id(),
1769 vec![],
1770 None,
1771 );
1772
1773 #[allow(clippy::needless_borrow)]
1774 let alice_request = VerificationRequest::from_request(
1775 VerificationCache::new(),
1776 alice_store,
1777 bob_id(),
1778 flow_id,
1779 &(&content).into(),
1780 bob_device_data.clone(),
1781 );
1782
1783 do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None);
1784
1785 let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
1786
1787 let content: OutgoingContent = request.try_into().unwrap();
1788 let content = StartContent::try_from(&content).unwrap();
1789 let flow_id = content.flow_id().to_owned();
1790 alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap();
1791 let alice_sas =
1792 alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap();
1793
1794 assert_let!(
1795 VerificationRequestState::Transitioned {
1796 verification: Verification::SasV1(_),
1797 other_device_data
1798 } = alice_request.state()
1799 );
1800
1801 assert_eq!(bob_device_data, other_device_data);
1802
1803 assert_let!(
1804 VerificationRequestState::Transitioned {
1805 verification: Verification::SasV1(_),
1806 other_device_data
1807 } = bob_request.state()
1808 );
1809
1810 assert_eq!(alice_device_data, other_device_data);
1811
1812 assert!(!bob_sas.is_cancelled());
1813 assert!(!alice_sas.is_cancelled());
1814 }
1815
1816 #[async_test]
1817 async fn test_requesting_until_sas_to_device() {
1818 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1819
1820 let alice_device_data = DeviceData::from_account(&alice);
1821 let bob_device_data = DeviceData::from_account(&bob);
1822
1823 let bob_request = build_test_request(&bob_store, alice_id(), None);
1825 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1826 do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None);
1827
1828 let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
1829
1830 let content: OutgoingContent = request.try_into().unwrap();
1831 let content = StartContent::try_from(&content).unwrap();
1832 let flow_id = content.flow_id().to_owned();
1833 alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap();
1834 let alice_sas =
1835 alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap();
1836
1837 assert_let!(
1838 VerificationRequestState::Transitioned {
1839 verification: Verification::SasV1(_),
1840 other_device_data
1841 } = alice_request.state()
1842 );
1843
1844 assert_eq!(bob_device_data, other_device_data);
1845
1846 assert_let!(
1847 VerificationRequestState::Transitioned {
1848 verification: Verification::SasV1(_),
1849 other_device_data
1850 } = bob_request.state()
1851 );
1852
1853 assert_eq!(alice_device_data, other_device_data);
1854
1855 assert!(!bob_sas.is_cancelled());
1856 assert!(!alice_sas.is_cancelled());
1857 assert!(alice_sas.started_from_request());
1858 assert!(bob_sas.started_from_request());
1859 }
1860
1861 #[async_test]
1862 #[cfg(feature = "qrcode")]
1863 async fn test_can_scan_another_qr_after_creating_mine() {
1864 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1865
1866 let alice_device_data = DeviceData::from_account(&alice);
1867 let bob_device_data = DeviceData::from_account(&bob);
1868
1869 let bob_request = build_test_request(
1871 &bob_store,
1872 alice_id(),
1873 Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]),
1874 );
1875 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1876 do_accept_request(
1877 &alice_request,
1878 alice_device_data.clone(),
1879 &bob_request,
1880 Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]),
1881 );
1882
1883 let alice_verification = alice_request.generate_qr_code().await.unwrap();
1885 let bob_verification = bob_request.generate_qr_code().await.unwrap();
1886
1887 assert_let!(
1888 VerificationRequestState::Transitioned {
1889 verification: Verification::QrV1(_),
1890 other_device_data
1891 } = alice_request.state()
1892 );
1893
1894 assert_eq!(bob_device_data, other_device_data);
1895
1896 assert_let!(
1897 VerificationRequestState::Transitioned {
1898 verification: Verification::QrV1(_),
1899 other_device_data
1900 } = bob_request.state()
1901 );
1902
1903 assert_eq!(alice_device_data, other_device_data);
1904
1905 assert!(alice_verification.is_some());
1906 assert!(bob_verification.is_some());
1907
1908 let bob_qr_code = bob_verification.unwrap().to_bytes().unwrap();
1910 let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap();
1911 let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap();
1912
1913 assert_let!(
1914 VerificationRequestState::Transitioned {
1915 verification: Verification::QrV1(alice_verification),
1916 other_device_data
1917 } = alice_request.state()
1918 );
1919
1920 assert_eq!(bob_device_data, other_device_data);
1921
1922 assert!(!alice_verification.is_cancelled());
1925 assert!(alice_verification.reciprocated());
1926 }
1927
1928 #[async_test]
1929 #[cfg(feature = "qrcode")]
1930 async fn test_can_start_sas_after_generating_qr_code() {
1931 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1932
1933 let alice_device_data = DeviceData::from_account(&alice);
1934 let bob_device_data = DeviceData::from_account(&bob);
1935
1936 let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods()));
1938 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1939 do_accept_request(
1940 &alice_request,
1941 alice_device_data.clone(),
1942 &bob_request,
1943 Some(all_methods()),
1944 );
1945
1946 let alice_verification = alice_request.generate_qr_code().await.unwrap();
1948 let bob_verification = bob_request.generate_qr_code().await.unwrap();
1949
1950 assert_let!(
1951 VerificationRequestState::Transitioned {
1952 verification: Verification::QrV1(_),
1953 other_device_data
1954 } = alice_request.state()
1955 );
1956
1957 assert_eq!(bob_device_data, other_device_data);
1958
1959 assert!(alice_verification.is_some());
1960 assert!(bob_verification.is_some());
1961
1962 let (sas, request) = alice_request.start_sas().await.unwrap().unwrap();
1965 assert_let!(
1966 VerificationRequestState::Transitioned {
1967 verification: Verification::SasV1(_),
1968 other_device_data
1969 } = alice_request.state()
1970 );
1971
1972 assert_eq!(bob_device_data, other_device_data);
1973 assert!(!sas.is_cancelled());
1974
1975 let content: OutgoingContent = request.try_into().unwrap();
1977 let content = StartContent::try_from(&content).unwrap();
1978 bob_request.receive_start(alice_id(), &content).await.unwrap();
1979
1980 assert_let!(
1982 VerificationRequestState::Transitioned {
1983 verification: Verification::SasV1(bob_sas),
1984 other_device_data
1985 } = bob_request.state()
1986 );
1987
1988 assert_eq!(alice_device_data, other_device_data);
1989
1990 assert!(!bob_sas.is_cancelled());
1992 }
1993
1994 #[async_test]
1995 #[cfg(feature = "qrcode")]
1996 async fn test_start_sas_after_scan_cancels_request() {
1997 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1998
1999 let alice_device_data = DeviceData::from_account(&alice);
2000 let bob_device_data = DeviceData::from_account(&bob);
2001
2002 let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods()));
2004 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
2005 do_accept_request(
2006 &alice_request,
2007 alice_device_data.clone(),
2008 &bob_request,
2009 Some(all_methods()),
2010 );
2011
2012 let bob_verification = bob_request.generate_qr_code().await.unwrap().unwrap();
2014 assert_let!(
2015 VerificationRequestState::Transitioned {
2016 verification: Verification::QrV1(_),
2017 other_device_data
2018 } = bob_request.state()
2019 );
2020
2021 assert_eq!(alice_device_data, other_device_data);
2022
2023 let bob_qr_code = bob_verification.to_bytes().unwrap();
2025 let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap();
2026 let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap();
2027
2028 assert_let!(
2029 VerificationRequestState::Transitioned {
2030 verification: Verification::QrV1(alice_qr),
2031 other_device_data
2032 } = alice_request.state()
2033 );
2034
2035 assert_eq!(bob_device_data, other_device_data);
2036 assert!(alice_qr.reciprocated());
2037
2038 let (_, request) = bob_request.start_sas().await.unwrap().unwrap();
2040 assert_let!(
2041 VerificationRequestState::Transitioned {
2042 verification: Verification::SasV1(_),
2043 other_device_data
2044 } = bob_request.state()
2045 );
2046
2047 assert_eq!(alice_device_data, other_device_data);
2048
2049 let content: OutgoingContent = request.try_into().unwrap();
2051 let content = StartContent::try_from(&content).unwrap();
2052 alice_request.receive_start(bob_id(), &content).await.unwrap();
2053
2054 assert!(alice_qr.is_cancelled());
2056
2057 assert_let!(
2059 VerificationRequestState::Transitioned {
2060 verification: Verification::SasV1(alice_sas),
2061 other_device_data
2062 } = alice_request.state()
2063 );
2064
2065 assert_eq!(bob_device_data, other_device_data);
2066 assert!(alice_sas.is_cancelled());
2067 }
2068
2069 fn build_test_request(
2079 verification_store: &VerificationStore,
2080 other_user_id: &UserId,
2081 methods: Option<Vec<VerificationMethod>>,
2082 ) -> VerificationRequest {
2083 VerificationRequest::new(
2084 VerificationCache::new(),
2085 verification_store.clone(),
2086 FlowId::ToDevice("TEST_FLOW_ID".into()),
2087 other_user_id,
2088 vec![],
2089 methods,
2090 )
2091 }
2092
2093 async fn build_incoming_verification_request(
2100 verification_store: &VerificationStore,
2101 outgoing_request: &VerificationRequest,
2102 ) -> VerificationRequest {
2103 let request = outgoing_request.request_to_device();
2104 let content: OutgoingContent = request.try_into().unwrap();
2105 let content = RequestContent::try_from(&content).unwrap();
2106
2107 let device_data = verification_store
2108 .get_device(outgoing_request.own_user_id(), content.from_device())
2109 .await
2110 .unwrap()
2111 .expect("Missing device data");
2112
2113 VerificationRequest::from_request(
2114 VerificationCache::new(),
2115 verification_store.clone(),
2116 outgoing_request.own_user_id(),
2117 outgoing_request.flow_id().clone(),
2118 &content,
2119 device_data,
2120 )
2121 }
2122
2123 fn do_accept_request(
2134 accepting_request: &VerificationRequest,
2135 accepting_device_data: DeviceData,
2136 initiating_request: &VerificationRequest,
2137 methods: Option<Vec<VerificationMethod>>,
2138 ) {
2139 let request = match methods {
2140 Some(methods) => accepting_request.accept_with_methods(methods),
2141 None => accepting_request.accept(),
2142 };
2143 let content: OutgoingContent = request.unwrap().try_into().unwrap();
2144 let content = ReadyContent::try_from(&content).unwrap();
2145 initiating_request.receive_ready(
2146 accepting_request.own_user_id(),
2147 &content,
2148 accepting_device_data,
2149 );
2150
2151 assert!(initiating_request.is_ready());
2152 assert!(accepting_request.is_ready());
2153 }
2154
2155 #[cfg(feature = "qrcode")]
2158 fn all_methods() -> Vec<VerificationMethod> {
2159 vec![
2160 VerificationMethod::SasV1,
2161 VerificationMethod::QrCodeScanV1,
2162 VerificationMethod::QrCodeShowV1,
2163 VerificationMethod::ReciprocateV1,
2164 ]
2165 }
2166}