matrix_sdk_crypto/verification/
qrcode.rs

1// Copyright 2021 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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/// An error for the different failure modes that can happen during the
61/// validation of a scanned QR code.
62#[derive(Debug, Error)]
63pub enum ScanError {
64    /// An IO error inside the crypto store happened during the validation of
65    /// the QR code scan.
66    #[error(transparent)]
67    Store(#[from] CryptoStoreError),
68    /// A key mismatch happened during the validation of the QR code scan.
69    #[error("The keys that are being verified didn't match (expected {expected}, found {found})")]
70    KeyMismatch {
71        /// The expected ed25519 key.
72        expected: String,
73        /// The ed25519 key that we got.
74        found: String,
75    },
76    /// One of the users that is participating in this verification doesn't have
77    /// a valid cross signing identity.
78    #[error("The user {0} is missing a valid cross signing identity")]
79    MissingCrossSigningIdentity(OwnedUserId),
80    /// The device of the user that is participating in this verification
81    /// doesn't have a valid device key.
82    #[error("The user's {0} device {1} is not E2E capable")]
83    MissingDeviceKeys(OwnedUserId, OwnedDeviceId),
84    /// The ID uniquely identifying this verification flow didn't match to the
85    /// one that has been scanned.
86    #[error("The unique verification flow id did not match (expected {expected}, found {found})")]
87    FlowIdMismatch {
88        /// The expected verification flow id.
89        expected: String,
90        /// The verification flow id that we instead got.
91        found: String,
92    },
93}
94
95/// An Enum describing the state the QrCode verification is in.
96#[derive(Debug, Clone)]
97pub enum QrVerificationState {
98    /// The QR verification has been started.
99    ///
100    /// We have received the other device's details (from the
101    /// `m.key.verification.request` or `m.key.verification.ready`) and
102    /// established the shared secret, so can
103    /// display the QR code.
104    ///
105    /// Note that despite the name of this state, we have not yet sent or
106    /// received an `m.key.verification.start` message.
107    Started,
108    /// The QR verification has been scanned by the other side.
109    Scanned,
110    /// We have confirmed the other side's scan of the QR code.
111    Confirmed,
112    /// We have successfully scanned the QR code and are able to send a
113    /// reciprocation event.
114    ///
115    /// Call `QrVerification::reciprocate` to build the reciprocation message.
116    ///
117    /// Note that, despite the name of this state, we have not necessarily
118    /// yet sent the `m.reciprocate.v1` message.
119    Reciprocated,
120    /// The verification process has been successfully concluded.
121    Done {
122        /// The list of devices that has been verified.
123        verified_devices: Vec<DeviceData>,
124        /// The list of user identities that has been verified.
125        verified_identities: Vec<UserIdentityData>,
126    },
127    /// The verification process has been cancelled.
128    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/// An object controlling QR code style key verification flows.
148#[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    /// Has the QR verification been scanned by the other side.
171    ///
172    /// When the verification object is in this state it's required that the
173    /// user confirms that the other side has scanned the QR code.
174    pub fn has_been_scanned(&self) -> bool {
175        matches!(*self.state.read(), InnerState::Scanned(_))
176    }
177
178    /// Has the scanning of the QR code been confirmed by us.
179    pub fn has_been_confirmed(&self) -> bool {
180        matches!(*self.state.read(), InnerState::Confirmed(_))
181    }
182
183    /// Get our own user id.
184    pub fn user_id(&self) -> &UserId {
185        self.identities.user_id()
186    }
187
188    /// Get the user id of the other user that is participating in this
189    /// verification flow.
190    pub fn other_user_id(&self) -> &UserId {
191        self.identities.other_user_id()
192    }
193
194    /// Get the device ID of the other side.
195    pub fn other_device_id(&self) -> &DeviceId {
196        self.identities.other_device_id()
197    }
198
199    /// Get the device of the other user.
200    pub fn other_device(&self) -> &DeviceData {
201        self.identities.other_device()
202    }
203
204    /// Did we initiate the verification request
205    pub fn we_started(&self) -> bool {
206        self.we_started
207    }
208
209    /// Get info about the cancellation if the verification flow has been
210    /// cancelled.
211    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    /// Has the verification flow completed.
218    pub fn is_done(&self) -> bool {
219        matches!(*self.state.read(), InnerState::Done(_))
220    }
221
222    /// Has the verification flow been cancelled.
223    pub fn is_cancelled(&self) -> bool {
224        matches!(*self.state.read(), InnerState::Cancelled(_))
225    }
226
227    /// Is this a verification that is verifying one of our own devices
228    pub fn is_self_verification(&self) -> bool {
229        self.identities.is_self_verification()
230    }
231
232    /// Have we successfully scanned the QR code and are able to send a
233    /// reciprocation event.
234    pub fn reciprocated(&self) -> bool {
235        matches!(*self.state.read(), InnerState::Reciprocated(_))
236    }
237
238    /// Get the unique ID that identifies this QR code verification flow.
239    pub fn flow_id(&self) -> &FlowId {
240        &self.flow_id
241    }
242
243    /// Get the room id if the verification is happening inside a room.
244    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    /// Generate a QR code object that is representing this verification flow.
252    ///
253    /// The `QrCode` can then be rendered as an image or as an unicode string.
254    ///
255    /// The [`to_bytes()`](#method.to_bytes) method can be used to instead
256    /// output the raw bytes that should be encoded as a QR code.
257    pub fn to_qr_code(&self) -> Result<QrCode, EncodingError> {
258        self.inner.to_qr_code()
259    }
260
261    /// Generate a the raw bytes that should be encoded as a QR code is
262    /// representing this verification flow.
263    ///
264    /// The [`to_qr_code()`](#method.to_qr_code) method can be used to instead
265    /// output a `QrCode` object that can be rendered.
266    pub fn to_bytes(&self) -> Result<Vec<u8>, EncodingError> {
267        self.inner.to_bytes()
268    }
269
270    /// Cancel the verification flow.
271    pub fn cancel(&self) -> Option<OutgoingVerificationRequest> {
272        self.cancel_with_code(CancelCode::User)
273    }
274
275    /// Cancel the verification.
276    ///
277    /// This cancels the verification with given `CancelCode`.
278    ///
279    /// **Note**: This method should generally not be used, the [`cancel()`]
280    /// method should be preferred. The SDK will automatically cancel with the
281    /// appropriate cancel code, user initiated cancellations should only cancel
282    /// with the `CancelCode::User`
283    ///
284    /// Returns None if the `Sas` object is already in a canceled state,
285    /// otherwise it returns a request that needs to be sent out.
286    ///
287    /// [`cancel()`]: #method.cancel
288    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    /// Notify the other side that we have successfully scanned the QR code and
312    /// that the QR verification flow can start.
313    ///
314    /// This will return some `OutgoingContent` if the object is in the correct
315    /// state to start the verification flow, otherwise `None`.
316    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    /// Confirm that the other side has scanned our QR code.
330    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    /// Listen for changes in the QrCode verification process.
682    ///
683    /// The changes are presented as a stream of [`QrVerificationState`] values.
684    pub fn changes(&self) -> impl Stream<Item = QrVerificationState> {
685        self.state.subscribe().map(|s| (&s).into())
686    }
687
688    /// Get the current state the verification process is in.
689    ///
690    /// To listen to changes to the [`QrVerificationState`] use the
691    /// [`QrVerification::changes`] method.
692    pub fn state(&self) -> QrVerificationState {
693        (&*self.state.read()).into()
694    }
695}
696
697#[derive(Debug, Clone)]
698enum InnerState {
699    /// We have received the other device's details (from the
700    /// `m.key.verification.request` or `m.key.verification.ready`) and
701    /// established the shared secret, so can
702    /// display the QR code.
703    Created(QrState<Created>),
704
705    /// The other side has scanned our QR code and sent an
706    /// `m.key.verification.start` message with `method: m.reciprocate.v1` with
707    /// matching shared secret.
708    Scanned(QrState<Scanned>),
709
710    /// Our user has confirmed that the other device scanned successfully. We
711    /// have sent an `m.key.verification.done`.
712    Confirmed(QrState<Confirmed>),
713
714    /// We have scanned the other side's QR code and are able to send a
715    /// `m.key.verification.start` message with `method: m.reciprocate.v1`.
716    ///
717    /// Call `QrVerification::reciprocate` to build the start message.
718    ///
719    /// Note that, despite the name of this state, we have not necessarily
720    /// yet sent the `m.reciprocate.v1` message.
721    Reciprocated(QrState<Reciprocated>),
722
723    /// Verification complete: we have received an `m.key.verification.done`
724    /// from the other side.
725    Done(QrState<Done>),
726
727    /// Verification cancelled or failed.
728    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                // TODO use constant time eq here.
810                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}