Skip to main content

matrix_sdk/encryption/verification/
requests.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 futures_util::{Stream, StreamExt};
16use matrix_sdk_base::crypto::{
17    CancelInfo, DeviceData, VerificationRequest as BaseVerificationRequest,
18};
19use ruma::{RoomId, events::key::verification::VerificationMethod};
20
21#[cfg(feature = "qrcode")]
22use super::{QrVerification, QrVerificationData};
23use super::{SasVerification, Verification};
24use crate::{Client, Result};
25
26/// An object controlling the interactive verification flow.
27#[derive(Debug, Clone)]
28pub struct VerificationRequest {
29    pub(crate) inner: BaseVerificationRequest,
30    pub(crate) client: Client,
31}
32
33/// An Enum describing the state the verification request is in.
34#[derive(Debug, Clone)]
35pub enum VerificationRequestState {
36    /// The verification request has been newly created by us.
37    Created {
38        /// The verification methods supported by us.
39        our_methods: Vec<VerificationMethod>,
40    },
41    /// The verification request was received from the other party.
42    Requested {
43        /// The verification methods supported by the sender.
44        their_methods: Vec<VerificationMethod>,
45
46        /// The device data of the device that responded to the verification
47        /// request.
48        other_device_data: DeviceData,
49    },
50    /// The verification request is ready to start a verification flow.
51    Ready {
52        /// The verification methods supported by the other side.
53        their_methods: Vec<VerificationMethod>,
54
55        /// The verification methods supported by the us.
56        our_methods: Vec<VerificationMethod>,
57
58        /// The device data of the device that responded to the verification
59        /// request.
60        other_device_data: DeviceData,
61    },
62    /// The verification request has transitioned into a concrete verification
63    /// flow. For example it transitioned into the emoji based SAS verification.
64    Transitioned {
65        /// The concrete [`Verification`] object the verification request
66        /// transitioned into.
67        verification: Verification,
68    },
69    /// The verification flow that was started with this request has finished.
70    Done,
71    /// The verification process has been cancelled.
72    Cancelled(CancelInfo),
73}
74
75impl VerificationRequest {
76    /// Has this verification finished.
77    pub fn is_done(&self) -> bool {
78        self.inner.is_done()
79    }
80
81    /// Has the verification been cancelled.
82    pub fn is_cancelled(&self) -> bool {
83        self.inner.is_cancelled()
84    }
85
86    /// Get the transaction id of this verification request
87    pub fn flow_id(&self) -> &str {
88        self.inner.flow_id().as_str()
89    }
90
91    /// Get info about the cancellation if the verification request has been
92    /// cancelled.
93    pub fn cancel_info(&self) -> Option<CancelInfo> {
94        self.inner.cancel_info()
95    }
96
97    /// Get our own user id.
98    pub fn own_user_id(&self) -> &ruma::UserId {
99        self.inner.own_user_id()
100    }
101
102    /// Has the verification request been answered by another device.
103    pub fn is_passive(&self) -> bool {
104        self.inner.is_passive()
105    }
106
107    /// Is the verification request ready to start a verification flow.
108    pub fn is_ready(&self) -> bool {
109        self.inner.is_ready()
110    }
111
112    /// Did we initiate the verification flow.
113    pub fn we_started(&self) -> bool {
114        self.inner.we_started()
115    }
116
117    /// Get the user id of the other user participating in this verification
118    /// flow.
119    pub fn other_user_id(&self) -> &ruma::UserId {
120        self.inner.other_user()
121    }
122
123    /// Is this a verification that is verifying one of our own devices.
124    pub fn is_self_verification(&self) -> bool {
125        self.inner.is_self_verification()
126    }
127
128    /// Get the supported verification methods of the other side.
129    ///
130    /// Will be present only if the other side requested the verification or if
131    /// we're in the ready state.
132    pub fn their_supported_methods(&self) -> Option<Vec<VerificationMethod>> {
133        self.inner.their_supported_methods()
134    }
135
136    /// Accept the verification request.
137    ///
138    /// This method will accept the request and signal by default that it
139    /// supports the `m.sas.v1`, the `m.qr_code.show.v1`, and `m.reciprocate.v1`
140    /// method. If the `qrcode` feature is disabled it will only signal that it
141    /// supports the `m.sas.v1` method.
142    ///
143    /// If QR code scanning should be supported or QR code showing shouldn't be
144    /// supported the [`accept_with_methods()`] method should be used instead.
145    ///
146    /// [`accept_with_methods()`]: #method.accept_with_methods
147    pub async fn accept(&self) -> Result<()> {
148        if let Some(request) = self.inner.accept() {
149            self.client.send_verification_request(request).await?;
150        }
151
152        Ok(())
153    }
154
155    /// Accept the verification request signaling that our client supports the
156    /// given verification methods.
157    ///
158    /// # Arguments
159    ///
160    /// - `methods` - The methods that we should advertise as supported by us.
161    pub async fn accept_with_methods(&self, methods: Vec<VerificationMethod>) -> Result<()> {
162        if let Some(request) = self.inner.accept_with_methods(methods) {
163            self.client.send_verification_request(request).await?;
164        }
165
166        Ok(())
167    }
168
169    /// Generate a QR code
170    #[cfg(feature = "qrcode")]
171    pub async fn generate_qr_code(&self) -> Result<Option<QrVerification>> {
172        Ok(self
173            .inner
174            .generate_qr_code()
175            .await?
176            .map(|qr| QrVerification { inner: Box::new(qr), client: self.client.clone() }))
177    }
178
179    /// Start a QR code verification by providing a scanned QR code for this
180    /// verification flow.
181    ///
182    /// Returns an `Error` if the QR code isn't valid or sending a reciprocate
183    /// event to the other side fails, `None` if the verification request isn't
184    /// in the ready state or we don't support QR code verification, otherwise a
185    /// newly created `QrVerification` object which will be used for the
186    /// remainder of the verification flow.
187    #[cfg(feature = "qrcode")]
188    pub async fn scan_qr_code(&self, data: QrVerificationData) -> Result<Option<QrVerification>> {
189        let Some(qr) = self.inner.scan_qr_code(data).await? else { return Ok(None) };
190        if let Some(request) = qr.reciprocate() {
191            self.client.send_verification_request(request).await?;
192        }
193
194        Ok(Some(QrVerification { inner: Box::new(qr), client: self.client.clone() }))
195    }
196
197    /// Transition from this verification request into a SAS verification flow.
198    pub async fn start_sas(&self) -> Result<Option<SasVerification>> {
199        let Some((sas, request)) = self.inner.start_sas().await? else { return Ok(None) };
200        self.client.send_verification_request(request).await?;
201
202        Ok(Some(SasVerification { inner: Box::new(sas), client: self.client.clone() }))
203    }
204
205    /// Cancel the verification request
206    pub async fn cancel(&self) -> Result<()> {
207        if let Some(request) = self.inner.cancel() {
208            self.client.send_verification_request(request).await?;
209        }
210
211        Ok(())
212    }
213
214    fn convert_state(
215        client: Client,
216        state: matrix_sdk_base::crypto::VerificationRequestState,
217    ) -> VerificationRequestState {
218        use matrix_sdk_base::crypto::VerificationRequestState::*;
219
220        match state {
221            Created { our_methods } => VerificationRequestState::Created { our_methods },
222            Requested { their_methods, other_device_data } => {
223                VerificationRequestState::Requested { their_methods, other_device_data }
224            }
225            Ready { their_methods, our_methods, other_device_data } => {
226                VerificationRequestState::Ready { their_methods, our_methods, other_device_data }
227            }
228            Transitioned { verification, .. } => VerificationRequestState::Transitioned {
229                verification: match verification {
230                    matrix_sdk_base::crypto::Verification::SasV1(sas) => {
231                        Verification::SasV1(SasVerification { inner: sas, client })
232                    }
233                    #[cfg(feature = "qrcode")]
234                    matrix_sdk_base::crypto::Verification::QrV1(qr) => {
235                        Verification::QrV1(QrVerification { inner: qr, client })
236                    }
237                    _ => unreachable!("We only support QR code and SAS verification"),
238                },
239            },
240            Done => VerificationRequestState::Done,
241            Cancelled(c) => VerificationRequestState::Cancelled(c),
242        }
243    }
244
245    /// Listen for changes in the verification request.
246    ///
247    /// The changes are presented as a stream of [`VerificationRequestState`]
248    /// values.
249    pub fn changes(&self) -> impl Stream<Item = VerificationRequestState> + use<> {
250        let client = self.client.to_owned();
251
252        self.inner.changes().map(move |s| Self::convert_state(client.to_owned(), s))
253    }
254
255    /// Get the current state the verification request is in.
256    ///
257    /// To listen to changes to the [`VerificationRequestState`] use the
258    /// [`VerificationRequest::changes`] method.
259    pub fn state(&self) -> VerificationRequestState {
260        Self::convert_state(self.client.to_owned(), self.inner.state())
261    }
262
263    /// Get the room ID, if the verification is happening inside a room.
264    pub fn room_id(&self) -> Option<&RoomId> {
265        self.inner.room_id()
266    }
267}