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