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