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}