1use std::sync::Arc;
16
17use as_variant::as_variant;
18use eyeball::{ObservableWriteGuard, SharedObservable};
19use futures_core::Stream;
20use futures_util::StreamExt;
21use matrix_sdk_qrcode::{
22 qrcode::QrCode, EncodingError, QrVerificationData, SelfVerificationData,
23 SelfVerificationNoMasterKey, VerificationData,
24};
25use rand::{thread_rng, RngCore};
26use ruma::{
27 api::client::keys::upload_signatures::v3::Request as SignatureUploadRequest,
28 events::{
29 key::verification::{
30 cancel::CancelCode,
31 done::{KeyVerificationDoneEventContent, ToDeviceKeyVerificationDoneEventContent},
32 start::{
33 KeyVerificationStartEventContent, ReciprocateV1Content, StartMethod,
34 ToDeviceKeyVerificationStartEventContent,
35 },
36 },
37 relation::Reference,
38 AnyMessageLikeEventContent, AnyToDeviceEventContent,
39 },
40 serde::Base64,
41 DeviceId, OwnedDeviceId, OwnedUserId, RoomId, TransactionId, UserId,
42};
43use thiserror::Error;
44use tracing::{debug, trace};
45use vodozemac::Ed25519PublicKey;
46
47use super::{
48 event_enums::{CancelContent, DoneContent, OutgoingContent, OwnedStartContent, StartContent},
49 requests::RequestHandle,
50 CancelInfo, Cancelled, Done, FlowId, IdentitiesBeingVerified, VerificationResult,
51 VerificationStore,
52};
53use crate::{
54 types::requests::{OutgoingVerificationRequest, RoomMessageRequest, ToDeviceRequest},
55 CryptoStoreError, DeviceData, UserIdentityData,
56};
57
58const SECRET_SIZE: usize = 16;
59
60#[derive(Debug, Error)]
63pub enum ScanError {
64 #[error(transparent)]
67 Store(#[from] CryptoStoreError),
68 #[error("The keys that are being verified didn't match (expected {expected}, found {found})")]
70 KeyMismatch {
71 expected: String,
73 found: String,
75 },
76 #[error("The user {0} is missing a valid cross signing identity")]
79 MissingCrossSigningIdentity(OwnedUserId),
80 #[error("The user's {0} device {1} is not E2E capable")]
83 MissingDeviceKeys(OwnedUserId, OwnedDeviceId),
84 #[error("The unique verification flow id did not match (expected {expected}, found {found})")]
87 FlowIdMismatch {
88 expected: String,
90 found: String,
92 },
93}
94
95#[derive(Debug, Clone)]
97pub enum QrVerificationState {
98 Started,
108 Scanned,
110 Confirmed,
112 Reciprocated,
120 Done {
122 verified_devices: Vec<DeviceData>,
124 verified_identities: Vec<UserIdentityData>,
126 },
127 Cancelled(CancelInfo),
129}
130
131impl From<&InnerState> for QrVerificationState {
132 fn from(value: &InnerState) -> Self {
133 match value {
134 InnerState::Created(_) => Self::Started,
135 InnerState::Scanned(_) => Self::Scanned,
136 InnerState::Confirmed(_) => Self::Confirmed,
137 InnerState::Reciprocated(_) => Self::Reciprocated,
138 InnerState::Done(s) => Self::Done {
139 verified_devices: s.state.verified_devices.to_vec(),
140 verified_identities: s.state.verified_master_keys.to_vec(),
141 },
142 InnerState::Cancelled(s) => Self::Cancelled(s.state.to_owned().into()),
143 }
144 }
145}
146
147#[derive(Clone)]
149pub struct QrVerification {
150 flow_id: FlowId,
151 inner: Arc<QrVerificationData>,
152 state: SharedObservable<InnerState>,
153 identities: IdentitiesBeingVerified,
154 request_handle: Option<RequestHandle>,
155 we_started: bool,
156}
157
158#[cfg(not(tarpaulin_include))]
159impl std::fmt::Debug for QrVerification {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.debug_struct("QrVerification")
162 .field("flow_id", &self.flow_id)
163 .field("inner", &self.inner)
164 .field("state", &self.state)
165 .finish()
166 }
167}
168
169impl QrVerification {
170 pub fn has_been_scanned(&self) -> bool {
175 matches!(*self.state.read(), InnerState::Scanned(_))
176 }
177
178 pub fn has_been_confirmed(&self) -> bool {
180 matches!(*self.state.read(), InnerState::Confirmed(_))
181 }
182
183 pub fn user_id(&self) -> &UserId {
185 self.identities.user_id()
186 }
187
188 pub fn other_user_id(&self) -> &UserId {
191 self.identities.other_user_id()
192 }
193
194 pub fn other_device_id(&self) -> &DeviceId {
196 self.identities.other_device_id()
197 }
198
199 pub fn other_device(&self) -> &DeviceData {
201 self.identities.other_device()
202 }
203
204 pub fn we_started(&self) -> bool {
206 self.we_started
207 }
208
209 pub fn cancel_info(&self) -> Option<CancelInfo> {
212 as_variant!(&*self.state.read(), InnerState::Cancelled(c) => {
213 c.state.clone().into()
214 })
215 }
216
217 pub fn is_done(&self) -> bool {
219 matches!(*self.state.read(), InnerState::Done(_))
220 }
221
222 pub fn is_cancelled(&self) -> bool {
224 matches!(*self.state.read(), InnerState::Cancelled(_))
225 }
226
227 pub fn is_self_verification(&self) -> bool {
229 self.identities.is_self_verification()
230 }
231
232 pub fn reciprocated(&self) -> bool {
235 matches!(*self.state.read(), InnerState::Reciprocated(_))
236 }
237
238 pub fn flow_id(&self) -> &FlowId {
240 &self.flow_id
241 }
242
243 pub fn room_id(&self) -> Option<&RoomId> {
245 match self.flow_id() {
246 FlowId::ToDevice(_) => None,
247 FlowId::InRoom(r, _) => Some(r),
248 }
249 }
250
251 pub fn to_qr_code(&self) -> Result<QrCode, EncodingError> {
258 self.inner.to_qr_code()
259 }
260
261 pub fn to_bytes(&self) -> Result<Vec<u8>, EncodingError> {
267 self.inner.to_bytes()
268 }
269
270 pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
272 self.cancel_with_code(CancelCode::User)
273 }
274
275 pub fn cancel_with_code(&self, code: CancelCode) -> Option<OutgoingVerificationRequest> {
289 let mut state = self.state.write();
290
291 if let Some(request) = &self.request_handle {
292 request.cancel_with_code(&code);
293 }
294
295 let new_state = QrState::<Cancelled>::new(true, code);
296 let content = new_state.as_content(self.flow_id());
297
298 match &*state {
299 InnerState::Confirmed(_)
300 | InnerState::Created(_)
301 | InnerState::Scanned(_)
302 | InnerState::Reciprocated(_)
303 | InnerState::Done(_) => {
304 ObservableWriteGuard::set(&mut state, InnerState::Cancelled(new_state));
305 Some(self.content_to_request(content))
306 }
307 InnerState::Cancelled(_) => None,
308 }
309 }
310
311 pub fn reciprocate(&self) -> Option<OutgoingVerificationRequest> {
317 match &*self.state.read() {
318 InnerState::Reciprocated(s) => {
319 Some(self.content_to_request(s.as_content(self.flow_id())))
320 }
321 InnerState::Created(_)
322 | InnerState::Scanned(_)
323 | InnerState::Confirmed(_)
324 | InnerState::Done(_)
325 | InnerState::Cancelled(_) => None,
326 }
327 }
328
329 pub fn confirm_scanning(&self) -> Option<OutgoingVerificationRequest> {
331 debug!("User confirmed other side scanned our QR code");
332 let mut state = self.state.write();
333
334 match &*state {
335 InnerState::Scanned(s) => {
336 let new_state = s.clone().confirm_scanning();
337 let content = new_state.as_content(&self.flow_id);
338 ObservableWriteGuard::set(&mut state, InnerState::Confirmed(new_state));
339
340 Some(self.content_to_request(content))
341 }
342 InnerState::Created(_)
343 | InnerState::Cancelled(_)
344 | InnerState::Confirmed(_)
345 | InnerState::Reciprocated(_)
346 | InnerState::Done(_) => None,
347 }
348 }
349
350 fn content_to_request(&self, content: OutgoingContent) -> OutgoingVerificationRequest {
351 match content {
352 OutgoingContent::Room(room_id, content) => {
353 RoomMessageRequest { room_id, txn_id: TransactionId::new(), content }.into()
354 }
355 OutgoingContent::ToDevice(c) => ToDeviceRequest::with_id(
356 self.identities.other_user_id(),
357 self.identities.other_device_id().to_owned(),
358 &c,
359 TransactionId::new(),
360 )
361 .into(),
362 }
363 }
364
365 async fn mark_as_done(
366 &self,
367 new_state: QrState<Done>,
368 ) -> Result<
369 (Option<OutgoingVerificationRequest>, Option<SignatureUploadRequest>),
370 CryptoStoreError,
371 > {
372 let (devices, identities) = new_state.verified_identities();
373
374 let mut new_state = InnerState::Done(new_state);
375
376 let (content, request) =
377 match self.identities.mark_as_done(Some(&devices), Some(&identities)).await? {
378 VerificationResult::Ok => (None, None),
379 VerificationResult::Cancel(c) => {
380 let canceled = QrState::<Cancelled>::new(false, c);
381 let content = canceled.as_content(self.flow_id());
382 new_state = InnerState::Cancelled(canceled);
383 (Some(content), None)
384 }
385 VerificationResult::SignatureUpload(s) => (None, Some(s)),
386 };
387
388 self.state.set(new_state);
389 Ok((content.map(|c| self.content_to_request(c)), request))
390 }
391
392 pub(crate) async fn receive_done(
393 &self,
394 content: &DoneContent<'_>,
395 ) -> Result<
396 (Option<OutgoingVerificationRequest>, Option<SignatureUploadRequest>),
397 CryptoStoreError,
398 > {
399 let state = self.state.get();
400
401 Ok(match state {
402 InnerState::Confirmed(s) => {
403 let (verified_device, verified_identity) = match &*self.inner {
404 QrVerificationData::Verification(_) => {
405 (None, self.identities.identity_being_verified.as_ref())
406 }
407 QrVerificationData::SelfVerification(_) => {
408 (Some(&self.identities.device_being_verified), None)
409 }
410 QrVerificationData::SelfVerificationNoMasterKey(_) => {
411 (None, self.identities.identity_being_verified.as_ref())
412 }
413 };
414
415 let new_state = s.clone().into_done(content, verified_device, verified_identity);
416 self.mark_as_done(new_state).await?
417 }
418 InnerState::Reciprocated(s) => {
419 let (verified_device, verified_identity) = match &*self.inner {
420 QrVerificationData::Verification(_) => {
421 (None, self.identities.identity_being_verified.as_ref())
422 }
423 QrVerificationData::SelfVerification(_) => {
424 (None, self.identities.identity_being_verified.as_ref())
425 }
426 QrVerificationData::SelfVerificationNoMasterKey(_) => {
427 (Some(&self.identities.device_being_verified), None)
428 }
429 };
430
431 let new_state = s.clone().into_done(content, verified_device, verified_identity);
432 let content = Some(new_state.as_content(self.flow_id()));
433 let (cancel_content, request) = self.mark_as_done(new_state).await?;
434
435 if cancel_content.is_some() {
436 (cancel_content, request)
437 } else {
438 (content.map(|c| self.content_to_request(c)), request)
439 }
440 }
441 InnerState::Created(_)
442 | InnerState::Scanned(_)
443 | InnerState::Done(_)
444 | InnerState::Cancelled(_) => (None, None),
445 })
446 }
447
448 pub(crate) fn receive_reciprocation(
449 &self,
450 content: &StartContent<'_>,
451 ) -> Option<OutgoingVerificationRequest> {
452 let mut state = self.state.write();
453
454 match &*state {
455 InnerState::Created(s) => match s.clone().receive_reciprocate(content) {
456 Ok(s) => {
457 ObservableWriteGuard::set(&mut state, InnerState::Scanned(s));
458 None
459 }
460 Err(s) => {
461 let content = s.as_content(self.flow_id());
462 ObservableWriteGuard::set(&mut state, InnerState::Cancelled(s));
463 Some(self.content_to_request(content))
464 }
465 },
466 InnerState::Confirmed(_)
467 | InnerState::Scanned(_)
468 | InnerState::Reciprocated(_)
469 | InnerState::Done(_)
470 | InnerState::Cancelled(_) => None,
471 }
472 }
473
474 pub(crate) fn receive_cancel(&self, sender: &UserId, content: &CancelContent<'_>) {
475 if sender == self.other_user_id() {
476 let mut state = self.state.write();
477
478 let new_state = match &*state {
479 InnerState::Created(s) => s.clone().into_cancelled(content),
480 InnerState::Scanned(s) => s.clone().into_cancelled(content),
481 InnerState::Confirmed(s) => s.clone().into_cancelled(content),
482 InnerState::Reciprocated(s) => s.clone().into_cancelled(content),
483 InnerState::Done(_) | InnerState::Cancelled(_) => return,
484 };
485
486 trace!(
487 ?sender,
488 code = content.cancel_code().as_str(),
489 "Cancelling a QR verification, other user has cancelled"
490 );
491
492 ObservableWriteGuard::set(&mut state, InnerState::Cancelled(new_state));
493 }
494 }
495
496 fn generate_secret() -> Base64 {
497 let mut shared_secret = vec![0u8; SECRET_SIZE];
498 let mut rng = thread_rng();
499 rng.fill_bytes(&mut shared_secret);
500
501 Base64::new(shared_secret)
502 }
503
504 pub(crate) fn new_self(
505 flow_id: FlowId,
506 own_master_key: Ed25519PublicKey,
507 other_device_key: Ed25519PublicKey,
508 identities: IdentitiesBeingVerified,
509 we_started: bool,
510 request_handle: Option<RequestHandle>,
511 ) -> Self {
512 let secret = Self::generate_secret();
513
514 let inner: QrVerificationData = SelfVerificationData::new(
515 flow_id.as_str().to_owned(),
516 own_master_key,
517 other_device_key,
518 secret,
519 )
520 .into();
521
522 Self::new_helper(flow_id, inner, identities, we_started, request_handle)
523 }
524
525 pub(crate) fn new_self_no_master(
526 store: VerificationStore,
527 flow_id: FlowId,
528 own_master_key: Ed25519PublicKey,
529 identities: IdentitiesBeingVerified,
530 we_started: bool,
531 request_handle: Option<RequestHandle>,
532 ) -> QrVerification {
533 let secret = Self::generate_secret();
534
535 let inner: QrVerificationData = SelfVerificationNoMasterKey::new(
536 flow_id.as_str().to_owned(),
537 store.account.identity_keys.ed25519,
538 own_master_key,
539 secret,
540 )
541 .into();
542
543 Self::new_helper(flow_id, inner, identities, we_started, request_handle)
544 }
545
546 pub(crate) fn new_cross(
547 flow_id: FlowId,
548 own_master_key: Ed25519PublicKey,
549 other_master_key: Ed25519PublicKey,
550 identities: IdentitiesBeingVerified,
551 we_started: bool,
552 request_handle: Option<RequestHandle>,
553 ) -> Self {
554 let secret = Self::generate_secret();
555
556 let inner: QrVerificationData = VerificationData::new(
557 flow_id.as_str().to_owned(),
558 own_master_key,
559 other_master_key,
560 secret,
561 )
562 .into();
563
564 Self::new_helper(flow_id, inner, identities, we_started, request_handle)
565 }
566
567 pub(crate) async fn from_scan(
568 store: VerificationStore,
569 other_user_id: OwnedUserId,
570 other_device_id: OwnedDeviceId,
571 flow_id: FlowId,
572 qr_code: QrVerificationData,
573 we_started: bool,
574 request_handle: Option<RequestHandle>,
575 ) -> Result<Self, ScanError> {
576 if flow_id.as_str() != qr_code.flow_id() {
577 return Err(ScanError::FlowIdMismatch {
578 expected: flow_id.as_str().to_owned(),
579 found: qr_code.flow_id().to_owned(),
580 });
581 }
582
583 let other_device =
584 store.get_device(&other_user_id, &other_device_id).await?.ok_or_else(|| {
585 ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone())
586 })?;
587
588 let identities = store.get_identities(other_device).await?;
589
590 let own_identity = identities
591 .own_identity
592 .as_ref()
593 .ok_or_else(|| ScanError::MissingCrossSigningIdentity(store.account.user_id.clone()))?;
594
595 let other_identity = identities
596 .identity_being_verified
597 .as_ref()
598 .ok_or_else(|| ScanError::MissingCrossSigningIdentity(other_user_id.clone()))?;
599
600 let check_master_key = |key, identity: &UserIdentityData| {
601 let master_key = identity.master_key().get_first_key().ok_or_else(|| {
602 ScanError::MissingCrossSigningIdentity(identity.user_id().to_owned())
603 })?;
604
605 if key != master_key {
606 Err(ScanError::KeyMismatch {
607 expected: master_key.to_base64(),
608 found: qr_code.first_key().to_base64(),
609 })
610 } else {
611 Ok(())
612 }
613 };
614
615 match qr_code {
616 QrVerificationData::Verification(_) => {
617 check_master_key(qr_code.first_key(), other_identity)?;
618 check_master_key(qr_code.second_key(), &own_identity.to_owned().into())?;
619 }
620 QrVerificationData::SelfVerification(_) => {
621 check_master_key(qr_code.first_key(), other_identity)?;
622 if qr_code.second_key() != store.account.identity_keys.ed25519 {
623 return Err(ScanError::KeyMismatch {
624 expected: store.account.identity_keys.ed25519.to_base64(),
625 found: qr_code.second_key().to_base64(),
626 });
627 }
628 }
629 QrVerificationData::SelfVerificationNoMasterKey(_) => {
630 let device_key =
631 identities.device_being_verified.ed25519_key().ok_or_else(|| {
632 ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone())
633 })?;
634
635 if qr_code.first_key() != device_key {
636 return Err(ScanError::KeyMismatch {
637 expected: device_key.to_base64(),
638 found: qr_code.first_key().to_base64(),
639 });
640 }
641 check_master_key(qr_code.second_key(), other_identity)?;
642 }
643 };
644
645 let secret = qr_code.secret().to_owned();
646 let own_device_id = store.account.device_id.clone();
647
648 Ok(Self {
649 flow_id,
650 inner: qr_code.into(),
651 state: SharedObservable::new(InnerState::Reciprocated(QrState {
652 state: Reciprocated { secret, own_device_id },
653 })),
654 identities,
655 we_started,
656 request_handle,
657 })
658 }
659
660 fn new_helper(
661 flow_id: FlowId,
662 inner: QrVerificationData,
663 identities: IdentitiesBeingVerified,
664 we_started: bool,
665 request_handle: Option<RequestHandle>,
666 ) -> Self {
667 let secret = inner.secret().to_owned();
668
669 Self {
670 flow_id,
671 inner: inner.into(),
672 state: SharedObservable::new(InnerState::Created(QrState {
673 state: Created { secret },
674 })),
675 identities,
676 we_started,
677 request_handle,
678 }
679 }
680
681 pub fn changes(&self) -> impl Stream<Item = QrVerificationState> {
685 self.state.subscribe().map(|s| (&s).into())
686 }
687
688 pub fn state(&self) -> QrVerificationState {
693 (&*self.state.read()).into()
694 }
695}
696
697#[derive(Debug, Clone)]
698enum InnerState {
699 Created(QrState<Created>),
704
705 Scanned(QrState<Scanned>),
709
710 Confirmed(QrState<Confirmed>),
713
714 Reciprocated(QrState<Reciprocated>),
722
723 Done(QrState<Done>),
726
727 Cancelled(QrState<Cancelled>),
729}
730
731#[derive(Clone, Debug)]
732struct QrState<S: Clone> {
733 state: S,
734}
735
736impl<S: Clone> QrState<S> {
737 pub fn into_cancelled(self, content: &CancelContent<'_>) -> QrState<Cancelled> {
738 QrState { state: Cancelled::new(false, content.cancel_code().to_owned()) }
739 }
740}
741
742#[derive(Clone, Debug)]
743struct Created {
744 secret: Base64,
745}
746
747#[derive(Clone, Debug)]
748struct Scanned {}
749
750#[derive(Clone, Debug)]
751struct Confirmed {}
752
753#[derive(Clone, Debug)]
754struct Reciprocated {
755 own_device_id: OwnedDeviceId,
756 secret: Base64,
757}
758
759impl Reciprocated {
760 fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
761 let content = ReciprocateV1Content::new(self.secret.clone());
762 let method = StartMethod::ReciprocateV1(content);
763
764 let content: OwnedStartContent = match flow_id {
765 FlowId::ToDevice(t) => ToDeviceKeyVerificationStartEventContent::new(
766 self.own_device_id.clone(),
767 t.clone(),
768 method,
769 )
770 .into(),
771 FlowId::InRoom(r, e) => (
772 r.clone(),
773 KeyVerificationStartEventContent::new(
774 self.own_device_id.clone(),
775 method,
776 Reference::new(e.clone()),
777 ),
778 )
779 .into(),
780 };
781
782 content.into()
783 }
784}
785
786impl QrState<Scanned> {
787 fn confirm_scanning(self) -> QrState<Confirmed> {
788 QrState { state: Confirmed {} }
789 }
790}
791
792impl QrState<Cancelled> {
793 fn new(cancelled_by_us: bool, cancel_code: CancelCode) -> Self {
794 QrState { state: Cancelled::new(cancelled_by_us, cancel_code) }
795 }
796
797 fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
798 self.state.as_content(flow_id)
799 }
800}
801
802impl QrState<Created> {
803 fn receive_reciprocate(
804 self,
805 content: &StartContent<'_>,
806 ) -> Result<QrState<Scanned>, QrState<Cancelled>> {
807 match content.method() {
808 StartMethod::ReciprocateV1(m) => {
809 if self.state.secret == m.secret {
811 Ok(QrState { state: Scanned {} })
812 } else {
813 Err(QrState::<Cancelled>::new(false, CancelCode::KeyMismatch))
814 }
815 }
816 _ => Err(QrState::<Cancelled>::new(false, CancelCode::UnknownMethod)),
817 }
818 }
819}
820
821impl QrState<Done> {
822 fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
823 self.state.as_content(flow_id)
824 }
825
826 fn verified_identities(&self) -> (Arc<[DeviceData]>, Arc<[UserIdentityData]>) {
827 (self.state.verified_devices.clone(), self.state.verified_master_keys.clone())
828 }
829}
830
831impl QrState<Confirmed> {
832 fn into_done(
833 self,
834 _: &DoneContent<'_>,
835 verified_device: Option<&DeviceData>,
836 verified_identity: Option<&UserIdentityData>,
837 ) -> QrState<Done> {
838 let devices: Vec<_> = verified_device.into_iter().cloned().collect();
839 let identities: Vec<_> = verified_identity.into_iter().cloned().collect();
840
841 QrState {
842 state: Done {
843 verified_devices: devices.into(),
844 verified_master_keys: identities.into(),
845 },
846 }
847 }
848
849 fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
850 match flow_id {
851 FlowId::ToDevice(t) => AnyToDeviceEventContent::KeyVerificationDone(
852 ToDeviceKeyVerificationDoneEventContent::new(t.to_owned()),
853 )
854 .into(),
855 FlowId::InRoom(r, e) => (
856 r.to_owned(),
857 AnyMessageLikeEventContent::KeyVerificationDone(
858 KeyVerificationDoneEventContent::new(Reference::new(e.to_owned())),
859 ),
860 )
861 .into(),
862 }
863 }
864}
865
866impl QrState<Reciprocated> {
867 fn as_content(&self, flow_id: &FlowId) -> OutgoingContent {
868 self.state.as_content(flow_id)
869 }
870
871 fn into_done(
872 self,
873 _: &DoneContent<'_>,
874 verified_device: Option<&DeviceData>,
875 verified_identity: Option<&UserIdentityData>,
876 ) -> QrState<Done> {
877 let devices: Vec<_> = verified_device.into_iter().cloned().collect();
878 let identities: Vec<_> = verified_identity.into_iter().cloned().collect();
879
880 QrState {
881 state: Done {
882 verified_devices: devices.into(),
883 verified_master_keys: identities.into(),
884 },
885 }
886 }
887}
888
889#[cfg(test)]
890mod tests {
891 use std::sync::Arc;
892
893 use assert_matches::assert_matches;
894 use matrix_sdk_qrcode::QrVerificationData;
895 use matrix_sdk_test::async_test;
896 use ruma::{device_id, event_id, room_id, user_id, DeviceId, UserId};
897 use tokio::sync::Mutex;
898
899 use crate::{
900 olm::{Account, PrivateCrossSigningIdentity},
901 store::{Changes, CryptoStoreWrapper, MemoryStore},
902 verification::{
903 event_enums::{DoneContent, OutgoingContent, StartContent},
904 FlowId, VerificationStore,
905 },
906 DeviceData, QrVerification, QrVerificationState,
907 };
908
909 fn user_id() -> &'static UserId {
910 user_id!("@example:localhost")
911 }
912
913 fn memory_store(user_id: &UserId, device_id: &DeviceId) -> Arc<CryptoStoreWrapper> {
914 Arc::new(CryptoStoreWrapper::new(user_id, device_id, MemoryStore::new()))
915 }
916
917 fn device_id() -> &'static DeviceId {
918 device_id!("DEVICEID")
919 }
920
921 #[async_test]
922 async fn test_verification_creation() {
923 let account = Account::with_device_id(user_id(), device_id());
924 let store = memory_store(account.user_id(), account.device_id());
925
926 let private_identity = PrivateCrossSigningIdentity::new(user_id().to_owned());
927 let master_key = private_identity.master_public_key().await.unwrap();
928 let master_key = master_key.get_first_key().unwrap().to_owned();
929
930 let store = VerificationStore {
931 account: account.static_data.clone(),
932 inner: store,
933 private_identity: Mutex::new(private_identity).into(),
934 };
935
936 let flow_id = FlowId::ToDevice("test_transaction".into());
937
938 let device_key = account.static_data.identity_keys.ed25519;
939 let alice_device = DeviceData::from_account(&account);
940
941 let identities = store.get_identities(alice_device).await.unwrap();
942
943 let verification = QrVerification::new_self_no_master(
944 store.clone(),
945 flow_id.clone(),
946 master_key,
947 identities.clone(),
948 false,
949 None,
950 );
951
952 assert_matches!(verification.state(), QrVerificationState::Started);
953 assert_eq!(verification.inner.first_key(), device_key);
954 assert_eq!(verification.inner.second_key(), master_key);
955
956 let verification = QrVerification::new_self(
957 flow_id,
958 master_key,
959 device_key,
960 identities.clone(),
961 false,
962 None,
963 );
964
965 assert_matches!(verification.state(), QrVerificationState::Started);
966 assert_eq!(verification.inner.first_key(), master_key);
967 assert_eq!(verification.inner.second_key(), device_key);
968
969 let bob_identity = PrivateCrossSigningIdentity::new(user_id!("@bob:example").to_owned());
970 let bob_master_key = bob_identity.master_public_key().await.unwrap();
971 let bob_master_key = bob_master_key.get_first_key().unwrap().to_owned();
972
973 let flow_id =
974 FlowId::InRoom(room_id!("!test:example").to_owned(), event_id!("$EVENTID").to_owned());
975
976 let verification =
977 QrVerification::new_cross(flow_id, master_key, bob_master_key, identities, false, None);
978
979 assert_matches!(verification.state(), QrVerificationState::Started);
980 assert_eq!(verification.inner.first_key(), master_key);
981 assert_eq!(verification.inner.second_key(), bob_master_key);
982 }
983
984 #[async_test]
985 async fn test_reciprocate_receival() {
986 let test = |flow_id: FlowId| async move {
987 let alice_account = Account::with_device_id(user_id(), device_id());
988 let store = memory_store(alice_account.user_id(), alice_account.device_id());
989
990 let private_identity = PrivateCrossSigningIdentity::new(user_id().to_owned());
991
992 let store = VerificationStore {
993 account: alice_account.static_data.clone(),
994 inner: store,
995 private_identity: Mutex::new(private_identity).into(),
996 };
997
998 let bob_account =
999 Account::with_device_id(alice_account.user_id(), device_id!("BOBDEVICE"));
1000
1001 let private_identity = PrivateCrossSigningIdentity::new(user_id().to_owned());
1002 let identity = private_identity.to_public_identity().await.unwrap();
1003
1004 let master_key = private_identity.master_public_key().await.unwrap();
1005 let master_key = master_key.get_first_key().unwrap().to_owned();
1006
1007 let alice_device = DeviceData::from_account(&alice_account);
1008 let bob_device = DeviceData::from_account(&bob_account);
1009
1010 let mut changes = Changes::default();
1011 changes.identities.new.push(identity.clone().into());
1012 changes.devices.new.push(bob_device.clone());
1013 store.save_changes(changes).await.unwrap();
1014
1015 let identities = store.get_identities(alice_device.clone()).await.unwrap();
1016
1017 let alice_verification = QrVerification::new_self_no_master(
1018 store,
1019 flow_id.clone(),
1020 master_key,
1021 identities,
1022 false,
1023 None,
1024 );
1025 assert_matches!(alice_verification.state(), QrVerificationState::Started);
1026
1027 let bob_store = memory_store(bob_account.user_id(), bob_account.device_id());
1028
1029 let private_identity = PrivateCrossSigningIdentity::new(user_id().to_owned());
1030 let bob_store = VerificationStore {
1031 account: bob_account.static_data.clone(),
1032 inner: bob_store,
1033 private_identity: Mutex::new(private_identity).into(),
1034 };
1035
1036 let mut changes = Changes::default();
1037 changes.identities.new.push(identity.into());
1038 changes.devices.new.push(alice_device.clone());
1039 bob_store.save_changes(changes).await.unwrap();
1040
1041 let qr_code = alice_verification.to_bytes().unwrap();
1042 let qr_code = QrVerificationData::from_bytes(qr_code).unwrap();
1043
1044 let bob_verification = QrVerification::from_scan(
1045 bob_store,
1046 alice_account.user_id().to_owned(),
1047 alice_account.device_id().to_owned(),
1048 flow_id,
1049 qr_code,
1050 false,
1051 None,
1052 )
1053 .await
1054 .unwrap();
1055
1056 let request = bob_verification.reciprocate().unwrap();
1057 assert_matches!(bob_verification.state(), QrVerificationState::Reciprocated);
1058
1059 let content = OutgoingContent::try_from(request).unwrap();
1060 let content = StartContent::try_from(&content).unwrap();
1061
1062 alice_verification.receive_reciprocation(&content);
1063 assert_matches!(alice_verification.state(), QrVerificationState::Scanned);
1064
1065 let request = alice_verification.confirm_scanning().unwrap();
1066 assert_matches!(alice_verification.state(), QrVerificationState::Confirmed);
1067
1068 let content = OutgoingContent::try_from(request).unwrap();
1069 let content = DoneContent::try_from(&content).unwrap();
1070
1071 assert!(!alice_verification.is_done());
1072 assert!(!bob_verification.is_done());
1073
1074 let (request, _) = bob_verification.receive_done(&content).await.unwrap();
1075 let content = OutgoingContent::try_from(request.unwrap()).unwrap();
1076 let content = DoneContent::try_from(&content).unwrap();
1077 alice_verification.receive_done(&content).await.unwrap();
1078
1079 assert_matches!(alice_verification.state(), QrVerificationState::Done { .. });
1080 assert_matches!(bob_verification.state(), QrVerificationState::Done { .. });
1081 assert!(alice_verification.is_done());
1082 assert!(bob_verification.is_done());
1083
1084 let identity = alice_verification
1085 .identities
1086 .store
1087 .get_user_identity(alice_account.user_id())
1088 .await
1089 .unwrap()
1090 .unwrap();
1091
1092 let identity = identity.own().unwrap();
1093
1094 assert!(!bob_device.is_locally_trusted());
1095 assert!(alice_device.is_locally_trusted());
1096 assert!(identity.is_verified());
1097 };
1098
1099 let flow_id = FlowId::ToDevice("test_transaction".into());
1100 test(flow_id).await;
1101
1102 let flow_id =
1103 FlowId::InRoom(room_id!("!test:example").to_owned(), event_id!("$EVENTID").to_owned());
1104 test(flow_id).await;
1105 }
1106}