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