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}