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_select! {
814 feature = "qrcode" => {
815 if self
816 .verification_cache
817 .get_qr(sas.other_user_id(), sas.flow_id().as_str())
818 .is_some()
819 {
820 debug!(
821 user_id = ?self.other_user(),
822 flow_id = self.flow_id().as_str(),
823 "We have an ongoing QR verification, replacing with SAS"
824 );
825 self.verification_cache.replace(sas.clone().into())
826 } else {
827 self.verification_cache.insert_sas(sas.clone());
828 }
829 }
830 _ => {
831 self.verification_cache.insert_sas(sas.clone());
832 }
833 }
834
835 let request = match content {
836 OutgoingContent::ToDevice(content) => ToDeviceRequest::with_id(
837 self.other_user(),
838 other_device_id,
839 &content,
840 TransactionId::new(),
841 )
842 .into(),
843 OutgoingContent::Room(room_id, content) => {
844 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
845 }
846 };
847
848 let mut guard = self.inner.write();
849 ObservableWriteGuard::set(&mut guard, InnerRequest::Transitioned(new_state));
850
851 Some((sas, request))
852 }
853
854 pub async fn start_sas(
856 &self,
857 ) -> Result<Option<(Sas, OutgoingVerificationRequest)>, CryptoStoreError> {
858 let inner = self.inner.get();
859 let other_device_id = inner.other_device_id();
860
861 Ok(match &inner {
862 InnerRequest::Ready(s) => {
863 if let Some((new_state, sas, content)) =
864 s.start_sas(self.we_started, self.inner.clone().into()).await?
865 {
866 self.start_sas_helper(new_state, sas, content, other_device_id)
867 } else {
868 None
869 }
870 }
871 InnerRequest::Transitioned(s) => {
872 if let Some((new_state, sas, content)) =
873 s.start_sas(self.we_started, self.inner.clone().into()).await?
874 {
875 self.start_sas_helper(new_state, sas, content, other_device_id)
876 } else {
877 None
878 }
879 }
880 _ => None,
881 })
882 }
883
884 pub fn changes(&self) -> impl Stream<Item = VerificationRequestState> + use<> {
889 self.inner.subscribe().map(|s| (&s).into())
890 }
891
892 pub fn state(&self) -> VerificationRequestState {
897 (&*self.inner.read()).into()
898 }
899}
900
901#[derive(Clone, Debug)]
902enum InnerRequest {
903 Created(RequestState<Created>),
904 Requested(RequestState<Requested>),
905 Ready(RequestState<Ready>),
906 Transitioned(RequestState<Transitioned>),
907 Passive(RequestState<Passive>),
908 #[allow(dead_code)] Done(RequestState<Done>),
910 Cancelled(RequestState<Cancelled>),
911}
912
913impl InnerRequest {
914 fn other_device_id(&self) -> DeviceIdOrAllDevices {
915 match self {
916 InnerRequest::Created(_) => DeviceIdOrAllDevices::AllDevices,
917 InnerRequest::Requested(r) => {
918 DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned())
919 }
920 InnerRequest::Ready(r) => {
921 DeviceIdOrAllDevices::DeviceId(r.state.other_device_data.device_id().to_owned())
922 }
923 InnerRequest::Transitioned(r) => DeviceIdOrAllDevices::DeviceId(
924 r.state.ready.other_device_data.device_id().to_owned(),
925 ),
926 InnerRequest::Passive(_) => DeviceIdOrAllDevices::AllDevices,
927 InnerRequest::Done(_) => DeviceIdOrAllDevices::AllDevices,
928 InnerRequest::Cancelled(_) => DeviceIdOrAllDevices::AllDevices,
929 }
930 }
931
932 fn accept(&self, methods: Vec<VerificationMethod>) -> Option<(InnerRequest, OutgoingContent)> {
933 let InnerRequest::Requested(s) = self else { return None };
934 let (state, content) = s.clone().accept(methods);
935
936 Some((InnerRequest::Ready(state), content))
937 }
938
939 fn receive_done(&self, content: &DoneContent<'_>) -> Option<InnerRequest> {
940 let state = InnerRequest::Done(match self {
941 InnerRequest::Transitioned(s) => s.clone().into_done(content),
942 InnerRequest::Passive(s) => s.clone().into_done(content),
943 InnerRequest::Done(_)
944 | InnerRequest::Ready(_)
945 | InnerRequest::Created(_)
946 | InnerRequest::Requested(_)
947 | InnerRequest::Cancelled(_) => return None,
948 });
949
950 Some(state)
951 }
952
953 fn cancel(&self, cancelled_by_us: bool, cancel_code: &CancelCode) -> Option<InnerRequest> {
954 let print_info = || {
955 trace!(
956 cancelled_by_us,
957 code = cancel_code.as_str(),
958 "Verification request going into the cancelled state"
959 );
960 };
961
962 let state = InnerRequest::Cancelled(match self {
963 InnerRequest::Created(s) => {
964 print_info();
965 s.clone().into_canceled(cancelled_by_us, cancel_code)
966 }
967 InnerRequest::Requested(s) => {
968 print_info();
969 s.clone().into_canceled(cancelled_by_us, cancel_code)
970 }
971 InnerRequest::Ready(s) => {
972 print_info();
973 s.clone().into_canceled(cancelled_by_us, cancel_code)
974 }
975 InnerRequest::Transitioned(s) => {
976 print_info();
977 s.clone().into_canceled(cancelled_by_us, cancel_code)
978 }
979 InnerRequest::Passive(_) | InnerRequest::Done(_) | InnerRequest::Cancelled(_) => {
980 return None;
981 }
982 });
983
984 Some(state)
985 }
986
987 #[cfg(feature = "qrcode")]
988 async fn generate_qr_code(
989 &self,
990 we_started: bool,
991 request_handle: RequestHandle,
992 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
993 match self {
994 InnerRequest::Created(_)
995 | InnerRequest::Requested(_)
996 | InnerRequest::Passive(_)
997 | InnerRequest::Done(_)
998 | InnerRequest::Cancelled(_) => Ok(None),
999 InnerRequest::Ready(s) => s.generate_qr_code(we_started, request_handle).await,
1000 InnerRequest::Transitioned(s) => s.generate_qr_code(we_started, request_handle).await,
1001 }
1002 }
1003}
1004
1005#[derive(Clone, Debug)]
1006struct RequestState<S: Clone> {
1007 verification_cache: VerificationCache,
1008 store: VerificationStore,
1009 flow_id: Arc<FlowId>,
1010
1011 pub other_user_id: OwnedUserId,
1013
1014 state: S,
1016}
1017
1018impl<S: Clone> RequestState<S> {
1019 fn into_done(self, _: &DoneContent<'_>) -> RequestState<Done> {
1020 RequestState::<Done> {
1021 verification_cache: self.verification_cache,
1022 store: self.store,
1023 flow_id: self.flow_id,
1024 other_user_id: self.other_user_id,
1025 state: Done {},
1026 }
1027 }
1028
1029 fn into_canceled(
1030 self,
1031 cancelled_by_us: bool,
1032 cancel_code: &CancelCode,
1033 ) -> RequestState<Cancelled> {
1034 RequestState::<Cancelled> {
1035 verification_cache: self.verification_cache,
1036 store: self.store,
1037 flow_id: self.flow_id,
1038 other_user_id: self.other_user_id,
1039 state: Cancelled::new(cancelled_by_us, cancel_code.clone()),
1040 }
1041 }
1042}
1043
1044impl RequestState<Created> {
1045 fn new(
1046 cache: VerificationCache,
1047 store: VerificationStore,
1048 other_user_id: &UserId,
1049 flow_id: &FlowId,
1050 methods: Option<Vec<VerificationMethod>>,
1051 ) -> Self {
1052 let our_methods = methods.unwrap_or_else(|| SUPPORTED_METHODS.to_vec());
1053
1054 Self {
1055 other_user_id: other_user_id.to_owned(),
1056 state: Created { our_methods },
1057 verification_cache: cache,
1058 store,
1059 flow_id: flow_id.to_owned().into(),
1060 }
1061 }
1062
1063 fn into_ready(
1064 self,
1065 _sender: &UserId,
1066 content: &ReadyContent<'_>,
1067 from_device_data: DeviceData,
1068 ) -> RequestState<Ready> {
1069 RequestState {
1071 flow_id: self.flow_id,
1072 verification_cache: self.verification_cache,
1073 store: self.store,
1074 other_user_id: self.other_user_id,
1075 state: Ready {
1076 their_methods: content.methods().to_owned(),
1077 our_methods: self.state.our_methods,
1078 other_device_data: from_device_data,
1079 },
1080 }
1081 }
1082}
1083
1084#[derive(Clone, Debug)]
1085struct Created {
1086 pub our_methods: Vec<VerificationMethod>,
1088}
1089
1090#[derive(Clone, Debug)]
1091struct Requested {
1092 pub their_methods: Vec<VerificationMethod>,
1094
1095 pub other_device_data: DeviceData,
1098}
1099
1100impl RequestState<Requested> {
1101 fn from_request_event(
1102 cache: VerificationCache,
1103 store: VerificationStore,
1104 sender: &UserId,
1105 flow_id: &FlowId,
1106 content: &RequestContent<'_>,
1107 device_data: DeviceData,
1108 ) -> RequestState<Requested> {
1109 RequestState {
1111 store,
1112 verification_cache: cache,
1113 flow_id: flow_id.to_owned().into(),
1114 other_user_id: sender.to_owned(),
1115 state: Requested {
1116 their_methods: content.methods().to_owned(),
1117 other_device_data: device_data,
1118 },
1119 }
1120 }
1121
1122 fn into_passive(self, content: &ReadyContent<'_>) -> RequestState<Passive> {
1123 RequestState {
1124 flow_id: self.flow_id,
1125 verification_cache: self.verification_cache,
1126 store: self.store,
1127 other_user_id: self.other_user_id,
1128 state: Passive { other_device_id: content.from_device().to_owned() },
1129 }
1130 }
1131
1132 fn accept(self, methods: Vec<VerificationMethod>) -> (RequestState<Ready>, OutgoingContent) {
1133 let state = RequestState {
1134 store: self.store,
1135 verification_cache: self.verification_cache,
1136 flow_id: self.flow_id.clone(),
1137 other_user_id: self.other_user_id,
1138 state: Ready {
1139 their_methods: self.state.their_methods,
1140 our_methods: methods.clone(),
1141 other_device_data: self.state.other_device_data,
1142 },
1143 };
1144
1145 let content = match self.flow_id.as_ref() {
1146 FlowId::ToDevice(i) => AnyToDeviceEventContent::KeyVerificationReady(
1147 ToDeviceKeyVerificationReadyEventContent::new(
1148 state.store.account.device_id.clone(),
1149 methods,
1150 i.to_owned(),
1151 ),
1152 )
1153 .into(),
1154 FlowId::InRoom(r, e) => (
1155 r.to_owned(),
1156 AnyMessageLikeEventContent::KeyVerificationReady(
1157 KeyVerificationReadyEventContent::new(
1158 state.store.account.device_id.clone(),
1159 methods,
1160 Reference::new(e.to_owned()),
1161 ),
1162 ),
1163 )
1164 .into(),
1165 };
1166
1167 (state, content)
1168 }
1169}
1170
1171#[derive(Clone, Debug)]
1172struct Ready {
1173 pub their_methods: Vec<VerificationMethod>,
1175
1176 pub our_methods: Vec<VerificationMethod>,
1178
1179 pub other_device_data: DeviceData,
1182}
1183
1184#[cfg(feature = "qrcode")]
1185async fn scan_qr_code<T: Clone>(
1186 data: QrVerificationData,
1187 request_state: &RequestState<T>,
1188 state: &Ready,
1189 we_started: bool,
1190 request_handle: RequestHandle,
1191) -> Result<(RequestState<Transitioned>, QrVerification), ScanError> {
1192 let verification = QrVerification::from_scan(
1193 request_state.store.to_owned(),
1194 request_state.other_user_id.to_owned(),
1195 state.other_device_data.device_id().to_owned(),
1196 request_state.flow_id.as_ref().to_owned(),
1197 data,
1198 we_started,
1199 Some(request_handle),
1200 )
1201 .await?;
1202
1203 let new_state = RequestState {
1204 verification_cache: request_state.verification_cache.to_owned(),
1205 store: request_state.store.to_owned(),
1206 flow_id: request_state.flow_id.to_owned(),
1207 other_user_id: request_state.other_user_id.to_owned(),
1208 state: Transitioned {
1209 ready: state.to_owned(),
1210 verification: verification.to_owned().into(),
1211 other_device_data: state.other_device_data.to_owned(),
1212 },
1213 };
1214
1215 Ok((new_state, verification))
1216}
1217
1218#[cfg(feature = "qrcode")]
1219async fn generate_qr_code<T: Clone>(
1220 request_state: &RequestState<T>,
1221 state: &Ready,
1222 we_started: bool,
1223 request_handle: RequestHandle,
1224) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1225 use crate::UserIdentityData;
1226
1227 if !state.our_methods.contains(&VerificationMethod::QrCodeShowV1)
1230 || !state.their_methods.contains(&VerificationMethod::QrCodeScanV1)
1231 {
1232 return Ok(None);
1233 }
1234
1235 let identities = request_state.store.get_identities(state.other_device_data.clone()).await?;
1236
1237 let verification = if let Some(identity) = &identities.identity_being_verified {
1238 match &identity {
1239 UserIdentityData::Own(i) => {
1240 if let Some(master_key) = i.master_key().get_first_key() {
1241 if identities.can_sign_devices().await {
1242 if let Some(device_key) = identities.other_device().ed25519_key() {
1243 Some(QrVerification::new_self(
1244 request_state.flow_id.as_ref().to_owned(),
1245 master_key.to_owned(),
1246 device_key.to_owned(),
1247 identities,
1248 we_started,
1249 Some(request_handle),
1250 ))
1251 } else {
1252 warn!(
1253 user_id = ?request_state.other_user_id,
1254 device_id = ?state.other_device_data.device_id(),
1255 "Can't create a QR code, the other device \
1256 doesn't have a valid device key"
1257 );
1258 None
1259 }
1260 } else {
1261 Some(QrVerification::new_self_no_master(
1262 request_state.store.clone(),
1263 request_state.flow_id.as_ref().to_owned(),
1264 master_key.to_owned(),
1265 identities,
1266 we_started,
1267 Some(request_handle),
1268 ))
1269 }
1270 } else {
1271 warn!(
1272 user_id = ?request_state.other_user_id,
1273 device_id = ?state.other_device_data.device_id(),
1274 "Can't create a QR code, our cross signing identity \
1275 doesn't contain a valid master key"
1276 );
1277 None
1278 }
1279 }
1280 UserIdentityData::Other(i) => {
1281 if let Some(other_master) = i.master_key().get_first_key() {
1282 if let Some(own_master) = identities
1286 .private_identity
1287 .master_public_key()
1288 .await
1289 .and_then(|m| m.get_first_key().map(|m| m.to_owned()))
1290 {
1291 Some(QrVerification::new_cross(
1292 request_state.flow_id.as_ref().to_owned(),
1293 own_master,
1294 other_master.to_owned(),
1295 identities,
1296 we_started,
1297 Some(request_handle),
1298 ))
1299 } else {
1300 warn!(
1301 user_id = ?request_state.other_user_id,
1302 device_id = ?state.other_device_data.device_id(),
1303 "Can't create a QR code, we don't trust our own \
1304 master key"
1305 );
1306 None
1307 }
1308 } else {
1309 warn!(
1310 user_id = ?request_state.other_user_id,
1311 device_id = ?state.other_device_data.device_id(),
1312 "Can't create a QR code, the user's identity \
1313 doesn't have a valid master key"
1314 );
1315 None
1316 }
1317 }
1318 }
1319 } else {
1320 warn!(
1321 user_id = ?request_state.other_user_id,
1322 device_id = ?state.other_device_data.device_id(),
1323 "Can't create a QR code, the user doesn't have a valid cross \
1324 signing identity."
1325 );
1326
1327 None
1328 };
1329
1330 if let Some(verification) = verification {
1331 let new_state = RequestState {
1332 verification_cache: request_state.verification_cache.to_owned(),
1333 store: request_state.store.to_owned(),
1334 flow_id: request_state.flow_id.to_owned(),
1335 other_user_id: request_state.other_user_id.to_owned(),
1336 state: Transitioned {
1337 ready: state.to_owned(),
1338 verification: verification.to_owned().into(),
1339 other_device_data: state.other_device_data.to_owned(),
1340 },
1341 };
1342
1343 request_state.verification_cache.insert_qr(verification.to_owned());
1344
1345 Ok(Some((new_state, verification)))
1346 } else {
1347 Ok(None)
1348 }
1349}
1350
1351async fn receive_start<T: Clone>(
1352 sender: &UserId,
1353 content: &StartContent<'_>,
1354 we_started: bool,
1355 request_handle: RequestHandle,
1356 request_state: &RequestState<T>,
1357 state: &Ready,
1358) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1359 info!(
1360 ?sender,
1361 device = ?content.from_device(),
1362 method = ?content.method(),
1363 "Received a new verification start event",
1364 );
1365
1366 let other_device_data = state.other_device_data.clone();
1367 let identities = request_state.store.get_identities(other_device_data.clone()).await?;
1368 let own_user_id = &request_state.store.account.user_id;
1369 let own_device_id = &request_state.store.account.device_id;
1370
1371 match content.method() {
1372 StartMethod::SasV1(_) => {
1373 match Sas::from_start_event(
1374 (*request_state.flow_id).to_owned(),
1375 content,
1376 identities,
1377 Some(request_handle),
1378 we_started,
1379 ) {
1380 Ok(new) => {
1381 let old_verification = request_state
1382 .verification_cache
1383 .get(sender, request_state.flow_id.as_str());
1384 match old_verification {
1385 Some(Verification::SasV1(_old)) => {
1386 use std::cmp::Ordering;
1392 if !matches!(
1393 (
1394 sender.cmp(own_user_id),
1395 other_device_data.device_id().cmp(own_device_id)
1396 ),
1397 (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater)
1398 ) {
1399 info!(
1400 "Started a new SAS verification, replacing an already started one."
1401 );
1402 request_state.verification_cache.replace_sas(new.to_owned());
1403 Ok(Some(state.to_transitioned(request_state, new.into())))
1404 } else {
1405 info!(
1406 "Ignored incoming SAS verification from lexicographically larger user/device ID."
1407 );
1408 Ok(None)
1409 }
1410 }
1411 #[cfg(feature = "qrcode")]
1412 Some(Verification::QrV1(old)) => {
1413 if let QrVerificationState::Started = old.state() {
1417 info!("Transitioned from QR display to SAS");
1420 request_state.verification_cache.replace_sas(new.to_owned());
1421 Ok(Some(state.to_transitioned(request_state, new.into())))
1422 } else {
1423 warn!(qr_state = ?old.state(), "Invalid transition from QR to SAS");
1430 request_state.verification_cache.insert_sas(new.to_owned());
1431 Ok(Some(state.to_transitioned(request_state, new.into())))
1432 }
1433 }
1434 None => {
1435 info!("Started a new SAS verification.");
1436 request_state.verification_cache.insert_sas(new.to_owned());
1437 Ok(Some(state.to_transitioned(request_state, new.into())))
1438 }
1439 }
1440 }
1441 Err(c) => {
1442 warn!(
1443 user_id = ?other_device_data.user_id(),
1444 device_id = ?other_device_data.device_id(),
1445 content = ?c,
1446 "Can't start key verification, canceling.",
1447 );
1448 request_state.verification_cache.queue_up_content(
1449 other_device_data.user_id(),
1450 other_device_data.device_id(),
1451 c,
1452 None,
1453 );
1454
1455 Ok(None)
1456 }
1457 }
1458 }
1459 #[cfg(feature = "qrcode")]
1460 StartMethod::ReciprocateV1(_) => {
1461 if let Some(qr_verification) =
1462 request_state.verification_cache.get_qr(sender, content.flow_id())
1463 {
1464 if let Some(request) = qr_verification.receive_reciprocation(content) {
1465 request_state.verification_cache.add_request(request.into())
1466 }
1467 debug!(
1468 sender = ?identities.device_being_verified.user_id(),
1469 device_id = ?identities.device_being_verified.device_id(),
1470 verification = ?qr_verification,
1471 "Received a QR code reciprocation"
1472 );
1473
1474 Ok(None)
1475 } else {
1476 warn!("Received a QR code reciprocation for an unknown flow");
1477 Ok(None)
1478 }
1479 }
1480 m => {
1481 warn!(method = ?m, "Received a key verification start event with an unsupported method");
1482 Ok(None)
1483 }
1484 }
1485}
1486
1487async fn start_sas<T: Clone>(
1488 request_state: &RequestState<T>,
1489 state: &Ready,
1490 we_started: bool,
1491 request_handle: RequestHandle,
1492) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1493 if !state.their_methods.contains(&VerificationMethod::SasV1) {
1494 return Ok(None);
1495 }
1496
1497 let identities = request_state.store.get_identities(state.other_device_data.clone()).await?;
1498
1499 let (state, sas, content) = match request_state.flow_id.as_ref() {
1500 FlowId::ToDevice(t) => {
1501 let (sas, content) =
1502 Sas::start(identities, t.to_owned(), we_started, Some(request_handle), None);
1503
1504 let state = Transitioned {
1505 ready: state.to_owned(),
1506 verification: sas.to_owned().into(),
1507 other_device_data: state.other_device_data.to_owned(),
1508 };
1509
1510 (state, sas, content)
1511 }
1512 FlowId::InRoom(r, e) => {
1513 let (sas, content) = Sas::start_in_room(
1514 e.to_owned(),
1515 r.to_owned(),
1516 identities,
1517 we_started,
1518 request_handle,
1519 );
1520 let state = Transitioned {
1521 ready: state.to_owned(),
1522 verification: sas.to_owned().into(),
1523 other_device_data: state.other_device_data.to_owned(),
1524 };
1525 (state, sas, content)
1526 }
1527 };
1528
1529 let state = RequestState {
1530 verification_cache: request_state.verification_cache.to_owned(),
1531 store: request_state.store.to_owned(),
1532 flow_id: request_state.flow_id.to_owned(),
1533 other_user_id: request_state.other_user_id.to_owned(),
1534 state,
1535 };
1536
1537 Ok(Some((state, sas, content)))
1538}
1539
1540impl RequestState<Ready> {
1541 #[cfg(feature = "qrcode")]
1542 async fn generate_qr_code(
1543 &self,
1544 we_started: bool,
1545 request_handle: RequestHandle,
1546 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1547 generate_qr_code(self, &self.state, we_started, request_handle).await
1548 }
1549
1550 async fn receive_start(
1551 &self,
1552 sender: &UserId,
1553 content: &StartContent<'_>,
1554 we_started: bool,
1555 request_handle: RequestHandle,
1556 ) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1557 receive_start(sender, content, we_started, request_handle, self, &self.state).await
1558 }
1559
1560 async fn start_sas(
1561 &self,
1562 we_started: bool,
1563 request_handle: RequestHandle,
1564 ) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1565 start_sas(self, &self.state, we_started, request_handle).await
1566 }
1567}
1568
1569impl Ready {
1570 fn to_transitioned<T: Clone>(
1571 &self,
1572 request_state: &RequestState<T>,
1573 verification: Verification,
1574 ) -> RequestState<Transitioned> {
1575 RequestState {
1576 verification_cache: request_state.verification_cache.to_owned(),
1577 store: request_state.store.to_owned(),
1578 flow_id: request_state.flow_id.to_owned(),
1579 other_user_id: request_state.other_user_id.to_owned(),
1580 state: Transitioned {
1581 ready: self.clone(),
1582 verification,
1583 other_device_data: self.other_device_data.clone(),
1584 },
1585 }
1586 }
1587}
1588
1589#[derive(Clone, Debug)]
1590struct Transitioned {
1591 ready: Ready,
1592 verification: Verification,
1593 other_device_data: DeviceData,
1594}
1595
1596impl RequestState<Transitioned> {
1597 #[cfg(feature = "qrcode")]
1598 async fn generate_qr_code(
1599 &self,
1600 we_started: bool,
1601 request_handle: RequestHandle,
1602 ) -> Result<Option<(RequestState<Transitioned>, QrVerification)>, CryptoStoreError> {
1603 generate_qr_code(self, &self.state.ready, we_started, request_handle).await
1604 }
1605
1606 async fn receive_start(
1607 &self,
1608 sender: &UserId,
1609 content: &StartContent<'_>,
1610 we_started: bool,
1611 request_handle: RequestHandle,
1612 ) -> Result<Option<RequestState<Transitioned>>, CryptoStoreError> {
1613 receive_start(sender, content, we_started, request_handle, self, &self.state.ready).await
1614 }
1615
1616 async fn start_sas(
1617 &self,
1618 we_started: bool,
1619 request_handle: RequestHandle,
1620 ) -> Result<Option<(RequestState<Transitioned>, Sas, OutgoingContent)>, CryptoStoreError> {
1621 start_sas(self, &self.state.ready, we_started, request_handle).await
1622 }
1623}
1624
1625#[derive(Clone, Debug)]
1626struct Passive {
1627 #[allow(dead_code)]
1629 pub other_device_id: OwnedDeviceId,
1630}
1631
1632#[derive(Clone, Debug)]
1633struct Done {}
1634
1635#[cfg(test)]
1636mod tests {
1637
1638 use std::time::Duration;
1639
1640 use assert_matches::assert_matches;
1641 #[cfg(feature = "qrcode")]
1642 use matrix_sdk_qrcode::QrVerificationData;
1643 use matrix_sdk_test::async_test;
1644 use ruma::{
1645 UserId, event_id, events::key::verification::VerificationMethod, owned_event_id,
1646 owned_room_id, room_id, to_device::DeviceIdOrAllDevices,
1647 };
1648 use strass::assert_let;
1649
1650 use super::VerificationRequest;
1651 use crate::{
1652 DeviceData, VerificationRequestState,
1653 types::requests::OutgoingVerificationRequest,
1654 verification::{
1655 FlowId, Verification, VerificationStore,
1656 cache::VerificationCache,
1657 event_enums::{
1658 CancelContent, OutgoingContent, ReadyContent, RequestContent, StartContent,
1659 },
1660 tests::{alice_id, bob_id, setup_stores},
1661 },
1662 };
1663
1664 #[async_test]
1665 async fn test_request_accepting() {
1666 let event_id = owned_event_id!("$1234localhost");
1667 let room_id = owned_room_id!("!test:localhost");
1668
1669 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1670
1671 let alice_device_data = DeviceData::from_account(&alice);
1672 let bob_device_data = DeviceData::from_account(&bob);
1673
1674 let content = VerificationRequest::request(
1675 &bob_store.account.user_id,
1676 &bob_store.account.device_id,
1677 alice_id(),
1678 None,
1679 );
1680
1681 let flow_id = FlowId::InRoom(room_id, event_id);
1682
1683 let bob_request = VerificationRequest::new(
1684 VerificationCache::new(),
1685 bob_store,
1686 flow_id.clone(),
1687 alice_id(),
1688 vec![],
1689 None,
1690 );
1691
1692 assert_matches!(bob_request.state(), VerificationRequestState::Created { .. });
1693 assert!(bob_request.time_remaining() <= Duration::from_secs(600)); assert!(bob_request.time_remaining() > Duration::from_secs(540)); #[allow(clippy::needless_borrow)]
1697 let alice_request = VerificationRequest::from_request(
1698 VerificationCache::new(),
1699 alice_store,
1700 bob_id(),
1701 flow_id,
1702 &(&content).into(),
1703 bob_device_data,
1704 );
1705
1706 assert_matches!(alice_request.state(), VerificationRequestState::Requested { .. });
1707
1708 let content: OutgoingContent = alice_request.accept().unwrap().try_into().unwrap();
1709 let content = ReadyContent::try_from(&content).unwrap();
1710
1711 bob_request.receive_ready(alice_id(), &content, alice_device_data);
1712
1713 assert_matches!(bob_request.state(), VerificationRequestState::Ready { .. });
1714 assert_matches!(alice_request.state(), VerificationRequestState::Ready { .. });
1715 assert!(bob_request.is_ready());
1716 assert!(alice_request.is_ready());
1717 }
1718
1719 #[async_test]
1720 async fn test_request_refusal_to_device() {
1721 let (_alice, alice_store, bob, bob_store) = setup_stores().await;
1724 let bob_device = DeviceData::from_account(&bob);
1725
1726 let bob_request = build_test_request(&bob_store, alice_id(), None);
1728 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1729
1730 let outgoing_request = alice_request.cancel().unwrap();
1731
1732 {
1734 assert_let!(
1735 OutgoingVerificationRequest::ToDevice(to_device_request) = &outgoing_request
1736 );
1737
1738 assert_eq!(to_device_request.messages.len(), 1);
1739 let device_ids: Vec<&DeviceIdOrAllDevices> =
1740 to_device_request.messages.values().next().unwrap().keys().collect();
1741 assert_eq!(device_ids.len(), 1);
1742
1743 assert_let!(DeviceIdOrAllDevices::DeviceId(device_id) = &device_ids[0]);
1744 assert_eq!(device_id, bob_device.device_id());
1745 }
1746
1747 let content = OutgoingContent::try_from(outgoing_request).unwrap();
1748 let content = CancelContent::try_from(&content).unwrap();
1749
1750 bob_request.receive_cancel(alice_id(), &content);
1751
1752 assert_matches!(bob_request.state(), VerificationRequestState::Cancelled { .. });
1753 assert_matches!(alice_request.state(), VerificationRequestState::Cancelled { .. });
1754 }
1755
1756 #[async_test]
1757 async fn test_requesting_until_sas() {
1758 let event_id = event_id!("$1234localhost");
1759 let room_id = room_id!("!test:localhost");
1760
1761 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1762
1763 let alice_device_data = DeviceData::from_account(&alice);
1764 let bob_device_data = DeviceData::from_account(&bob);
1765
1766 let content = VerificationRequest::request(
1767 &bob_store.account.user_id,
1768 &bob_store.account.device_id,
1769 alice_id(),
1770 None,
1771 );
1772
1773 let flow_id = FlowId::from((room_id, event_id));
1774
1775 let bob_request = VerificationRequest::new(
1776 VerificationCache::new(),
1777 bob_store,
1778 flow_id.clone(),
1779 alice_id(),
1780 vec![],
1781 None,
1782 );
1783
1784 #[allow(clippy::needless_borrow)]
1785 let alice_request = VerificationRequest::from_request(
1786 VerificationCache::new(),
1787 alice_store,
1788 bob_id(),
1789 flow_id,
1790 &(&content).into(),
1791 bob_device_data.clone(),
1792 );
1793
1794 do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None);
1795
1796 let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
1797
1798 let content: OutgoingContent = request.try_into().unwrap();
1799 let content = StartContent::try_from(&content).unwrap();
1800 let flow_id = content.flow_id().to_owned();
1801 alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap();
1802 let alice_sas =
1803 alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap();
1804
1805 assert_let!(
1806 VerificationRequestState::Transitioned {
1807 verification: Verification::SasV1(_),
1808 other_device_data
1809 } = alice_request.state()
1810 );
1811
1812 assert_eq!(bob_device_data, other_device_data);
1813
1814 assert_let!(
1815 VerificationRequestState::Transitioned {
1816 verification: Verification::SasV1(_),
1817 other_device_data
1818 } = bob_request.state()
1819 );
1820
1821 assert_eq!(alice_device_data, other_device_data);
1822
1823 assert!(!bob_sas.is_cancelled());
1824 assert!(!alice_sas.is_cancelled());
1825 }
1826
1827 #[async_test]
1828 async fn test_requesting_until_sas_to_device() {
1829 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1830
1831 let alice_device_data = DeviceData::from_account(&alice);
1832 let bob_device_data = DeviceData::from_account(&bob);
1833
1834 let bob_request = build_test_request(&bob_store, alice_id(), None);
1836 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1837 do_accept_request(&alice_request, alice_device_data.clone(), &bob_request, None);
1838
1839 let (bob_sas, request) = bob_request.start_sas().await.unwrap().unwrap();
1840
1841 let content: OutgoingContent = request.try_into().unwrap();
1842 let content = StartContent::try_from(&content).unwrap();
1843 let flow_id = content.flow_id().to_owned();
1844 alice_request.receive_start(bob_device_data.user_id(), &content).await.unwrap();
1845 let alice_sas =
1846 alice_request.verification_cache.get_sas(bob_device_data.user_id(), &flow_id).unwrap();
1847
1848 assert_let!(
1849 VerificationRequestState::Transitioned {
1850 verification: Verification::SasV1(_),
1851 other_device_data
1852 } = alice_request.state()
1853 );
1854
1855 assert_eq!(bob_device_data, other_device_data);
1856
1857 assert_let!(
1858 VerificationRequestState::Transitioned {
1859 verification: Verification::SasV1(_),
1860 other_device_data
1861 } = bob_request.state()
1862 );
1863
1864 assert_eq!(alice_device_data, other_device_data);
1865
1866 assert!(!bob_sas.is_cancelled());
1867 assert!(!alice_sas.is_cancelled());
1868 assert!(alice_sas.started_from_request());
1869 assert!(bob_sas.started_from_request());
1870 }
1871
1872 #[async_test]
1873 #[cfg(feature = "qrcode")]
1874 async fn test_can_scan_another_qr_after_creating_mine() {
1875 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1876
1877 let alice_device_data = DeviceData::from_account(&alice);
1878 let bob_device_data = DeviceData::from_account(&bob);
1879
1880 let bob_request = build_test_request(
1882 &bob_store,
1883 alice_id(),
1884 Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]),
1885 );
1886 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1887 do_accept_request(
1888 &alice_request,
1889 alice_device_data.clone(),
1890 &bob_request,
1891 Some(vec![VerificationMethod::QrCodeScanV1, VerificationMethod::QrCodeShowV1]),
1892 );
1893
1894 let alice_verification = alice_request.generate_qr_code().await.unwrap();
1897 let bob_verification = bob_request.generate_qr_code().await.unwrap();
1898
1899 assert_let!(
1900 VerificationRequestState::Transitioned {
1901 verification: Verification::QrV1(_),
1902 other_device_data
1903 } = alice_request.state()
1904 );
1905
1906 assert_eq!(bob_device_data, other_device_data);
1907
1908 assert_let!(
1909 VerificationRequestState::Transitioned {
1910 verification: Verification::QrV1(_),
1911 other_device_data
1912 } = bob_request.state()
1913 );
1914
1915 assert_eq!(alice_device_data, other_device_data);
1916
1917 assert!(alice_verification.is_some());
1918 assert!(bob_verification.is_some());
1919
1920 let bob_qr_code = bob_verification.unwrap().to_bytes().unwrap();
1922 let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap();
1923 let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap();
1924
1925 assert_let!(
1926 VerificationRequestState::Transitioned {
1927 verification: Verification::QrV1(alice_verification),
1928 other_device_data
1929 } = alice_request.state()
1930 );
1931
1932 assert_eq!(bob_device_data, other_device_data);
1933
1934 assert!(!alice_verification.is_cancelled());
1937 assert!(alice_verification.reciprocated());
1938 }
1939
1940 #[async_test]
1941 #[cfg(feature = "qrcode")]
1942 async fn test_can_start_sas_after_generating_qr_code() {
1943 let (alice, alice_store, bob, bob_store) = setup_stores().await;
1944
1945 let alice_device_data = DeviceData::from_account(&alice);
1946 let bob_device_data = DeviceData::from_account(&bob);
1947
1948 let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods()));
1950 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
1951 do_accept_request(
1952 &alice_request,
1953 alice_device_data.clone(),
1954 &bob_request,
1955 Some(all_methods()),
1956 );
1957
1958 let alice_verification = alice_request.generate_qr_code().await.unwrap();
1961 let bob_verification = bob_request.generate_qr_code().await.unwrap();
1962
1963 assert_let!(
1964 VerificationRequestState::Transitioned {
1965 verification: Verification::QrV1(_),
1966 other_device_data
1967 } = alice_request.state()
1968 );
1969
1970 assert_eq!(bob_device_data, other_device_data);
1971
1972 assert!(alice_verification.is_some());
1973 assert!(bob_verification.is_some());
1974
1975 let (sas, request) = alice_request.start_sas().await.unwrap().unwrap();
1978 assert_let!(
1979 VerificationRequestState::Transitioned {
1980 verification: Verification::SasV1(_),
1981 other_device_data
1982 } = alice_request.state()
1983 );
1984
1985 assert_eq!(bob_device_data, other_device_data);
1986 assert!(!sas.is_cancelled());
1987
1988 let content: OutgoingContent = request.try_into().unwrap();
1990 let content = StartContent::try_from(&content).unwrap();
1991 bob_request.receive_start(alice_id(), &content).await.unwrap();
1992
1993 assert_let!(
1995 VerificationRequestState::Transitioned {
1996 verification: Verification::SasV1(bob_sas),
1997 other_device_data
1998 } = bob_request.state()
1999 );
2000
2001 assert_eq!(alice_device_data, other_device_data);
2002
2003 assert!(!bob_sas.is_cancelled());
2005 }
2006
2007 #[async_test]
2008 #[cfg(feature = "qrcode")]
2009 async fn test_start_sas_after_scan_cancels_request() {
2010 let (alice, alice_store, bob, bob_store) = setup_stores().await;
2011
2012 let alice_device_data = DeviceData::from_account(&alice);
2013 let bob_device_data = DeviceData::from_account(&bob);
2014
2015 let bob_request = build_test_request(&bob_store, alice_id(), Some(all_methods()));
2017 let alice_request = build_incoming_verification_request(&alice_store, &bob_request).await;
2018 do_accept_request(
2019 &alice_request,
2020 alice_device_data.clone(),
2021 &bob_request,
2022 Some(all_methods()),
2023 );
2024
2025 let bob_verification = bob_request.generate_qr_code().await.unwrap().unwrap();
2027 assert_let!(
2028 VerificationRequestState::Transitioned {
2029 verification: Verification::QrV1(_),
2030 other_device_data
2031 } = bob_request.state()
2032 );
2033
2034 assert_eq!(alice_device_data, other_device_data);
2035
2036 let bob_qr_code = bob_verification.to_bytes().unwrap();
2038 let bob_qr_code = QrVerificationData::from_bytes(bob_qr_code).unwrap();
2039 let _ = alice_request.scan_qr_code(bob_qr_code).await.unwrap().unwrap();
2040
2041 assert_let!(
2042 VerificationRequestState::Transitioned {
2043 verification: Verification::QrV1(alice_qr),
2044 other_device_data
2045 } = alice_request.state()
2046 );
2047
2048 assert_eq!(bob_device_data, other_device_data);
2049 assert!(alice_qr.reciprocated());
2050
2051 let (_, request) = bob_request.start_sas().await.unwrap().unwrap();
2053 assert_let!(
2054 VerificationRequestState::Transitioned {
2055 verification: Verification::SasV1(_),
2056 other_device_data
2057 } = bob_request.state()
2058 );
2059
2060 assert_eq!(alice_device_data, other_device_data);
2061
2062 let content: OutgoingContent = request.try_into().unwrap();
2064 let content = StartContent::try_from(&content).unwrap();
2065 alice_request.receive_start(bob_id(), &content).await.unwrap();
2066
2067 assert!(alice_qr.is_cancelled());
2069
2070 assert_let!(
2072 VerificationRequestState::Transitioned {
2073 verification: Verification::SasV1(alice_sas),
2074 other_device_data
2075 } = alice_request.state()
2076 );
2077
2078 assert_eq!(bob_device_data, other_device_data);
2079 assert!(alice_sas.is_cancelled());
2080 }
2081
2082 fn build_test_request(
2092 verification_store: &VerificationStore,
2093 other_user_id: &UserId,
2094 methods: Option<Vec<VerificationMethod>>,
2095 ) -> VerificationRequest {
2096 VerificationRequest::new(
2097 VerificationCache::new(),
2098 verification_store.clone(),
2099 FlowId::ToDevice("TEST_FLOW_ID".into()),
2100 other_user_id,
2101 vec![],
2102 methods,
2103 )
2104 }
2105
2106 async fn build_incoming_verification_request(
2113 verification_store: &VerificationStore,
2114 outgoing_request: &VerificationRequest,
2115 ) -> VerificationRequest {
2116 let request = outgoing_request.request_to_device();
2117 let content: OutgoingContent = request.try_into().unwrap();
2118 let content = RequestContent::try_from(&content).unwrap();
2119
2120 let device_data = verification_store
2121 .get_device(outgoing_request.own_user_id(), content.from_device())
2122 .await
2123 .unwrap()
2124 .expect("Missing device data");
2125
2126 VerificationRequest::from_request(
2127 VerificationCache::new(),
2128 verification_store.clone(),
2129 outgoing_request.own_user_id(),
2130 outgoing_request.flow_id().clone(),
2131 &content,
2132 device_data,
2133 )
2134 }
2135
2136 fn do_accept_request(
2147 accepting_request: &VerificationRequest,
2148 accepting_device_data: DeviceData,
2149 initiating_request: &VerificationRequest,
2150 methods: Option<Vec<VerificationMethod>>,
2151 ) {
2152 let request = match methods {
2153 Some(methods) => accepting_request.accept_with_methods(methods),
2154 None => accepting_request.accept(),
2155 };
2156 let content: OutgoingContent = request.unwrap().try_into().unwrap();
2157 let content = ReadyContent::try_from(&content).unwrap();
2158 initiating_request.receive_ready(
2159 accepting_request.own_user_id(),
2160 &content,
2161 accepting_device_data,
2162 );
2163
2164 assert!(initiating_request.is_ready());
2165 assert!(accepting_request.is_ready());
2166 }
2167
2168 #[cfg(feature = "qrcode")]
2171 fn all_methods() -> Vec<VerificationMethod> {
2172 vec![
2173 VerificationMethod::SasV1,
2174 VerificationMethod::QrCodeScanV1,
2175 VerificationMethod::QrCodeShowV1,
2176 VerificationMethod::ReciprocateV1,
2177 ]
2178 }
2179}