matrix_sdk/encryption/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 futures_core::Stream;
16use matrix_sdk_base::crypto::{
17 CancelInfo, DeviceData, QrVerification as BaseQrVerification, QrVerificationState,
18 matrix_sdk_qrcode::{EncodingError, qrcode::QrCode},
19};
20use ruma::{RoomId, UserId};
21
22use crate::{Client, Result};
23
24/// An object controlling QR code style key verification flows.
25#[derive(Debug, Clone)]
26pub struct QrVerification {
27 pub(crate) inner: Box<BaseQrVerification>,
28 pub(crate) client: Client,
29}
30
31impl QrVerification {
32 /// Get our own user id.
33 pub fn own_user_id(&self) -> &UserId {
34 self.inner.user_id()
35 }
36
37 /// Is this a verification that is verifying one of our own devices.
38 pub fn is_self_verification(&self) -> bool {
39 self.inner.is_self_verification()
40 }
41
42 /// Has this verification finished.
43 pub fn is_done(&self) -> bool {
44 self.inner.is_done()
45 }
46
47 /// Whether the QrCode was scanned by the other device.
48 pub fn has_been_scanned(&self) -> bool {
49 self.inner.has_been_scanned()
50 }
51
52 /// Did we initiate the verification flow.
53 pub fn we_started(&self) -> bool {
54 self.inner.we_started()
55 }
56
57 /// Get info about the cancellation if the verification flow has been
58 /// cancelled.
59 pub fn cancel_info(&self) -> Option<CancelInfo> {
60 self.inner.cancel_info()
61 }
62
63 /// Get the user id of the other user participating in this verification
64 /// flow.
65 pub fn other_user_id(&self) -> &UserId {
66 self.inner.other_user_id()
67 }
68
69 /// Get the other user's device that we're verifying.
70 pub fn other_device(&self) -> &DeviceData {
71 self.inner.other_device()
72 }
73
74 /// Has the verification been cancelled.
75 pub fn is_cancelled(&self) -> bool {
76 self.inner.is_cancelled()
77 }
78
79 /// Generate a QR code object that is representing this verification flow.
80 ///
81 /// The `QrCode` can then be rendered as an image or as an unicode string.
82 ///
83 /// The [`to_bytes()`](#method.to_bytes) method can be used to instead
84 /// output the raw bytes that should be encoded as a QR code.
85 pub fn to_qr_code(&self) -> Result<QrCode, EncodingError> {
86 self.inner.to_qr_code()
87 }
88
89 /// Generate a the raw bytes that should be encoded as a QR code is
90 /// representing this verification flow.
91 ///
92 /// The [`to_qr_code()`](#method.to_qr_code) method can be used to instead
93 /// output a `QrCode` object that can be rendered.
94 pub fn to_bytes(&self) -> Result<Vec<u8>, EncodingError> {
95 self.inner.to_bytes()
96 }
97
98 /// Confirm that the other side has scanned our QR code.
99 pub async fn confirm(&self) -> Result<()> {
100 if let Some(request) = self.inner.confirm_scanning() {
101 self.client.send_verification_request(request).await?;
102 }
103
104 Ok(())
105 }
106
107 /// Abort the verification flow and notify the other side that we did so.
108 pub async fn cancel(&self) -> Result<()> {
109 if let Some(request) = self.inner.cancel() {
110 self.client.send_verification_request(request).await?;
111 }
112
113 Ok(())
114 }
115
116 /// Listen for changes in the QR code verification process.
117 ///
118 /// The changes are presented as a stream of [`QrVerificationState`] values.
119 ///
120 /// This method can be used to react to changes in the state of the
121 /// verification process, or rather the method can be used to handle each
122 /// step of the verification process.
123 ///
124 /// # Flowchart
125 ///
126 /// The flow of the verification process is pictured below. Please note that
127 /// the process can be cancelled at each step of the process. Either side
128 /// can cancel the process.
129 ///
130 /// ```text
131 /// ┌───────┐
132 /// │Started│
133 /// └───┬───┘
134 /// │
135 /// │
136 /// ┌──────⌄─────┐
137 /// │Reciprocated│
138 /// └──────┬─────┘
139 /// │
140 /// ┌───⌄───┐
141 /// │Scanned│
142 /// └───┬───┘
143 /// │
144 /// __________⌄_________
145 /// ╱ ╲ ┌─────────┐
146 /// ╱ Was the QR Code ╲______│Cancelled│
147 /// ╲ successfully scanned ╱ no └─────────┘
148 /// ╲____________________╱
149 /// │yes
150 /// │
151 /// ┌────⌄────┐
152 /// │Confirmed│
153 /// └────┬────┘
154 /// │
155 /// ┌───⌄───┐
156 /// │ Done │
157 /// └───────┘
158 /// ```
159 ///
160 /// # Examples
161 ///
162 /// ```no_run
163 /// use futures_util::{Stream, StreamExt};
164 /// use matrix_sdk::encryption::verification::{
165 /// QrVerification, QrVerificationState,
166 /// };
167 ///
168 /// # async {
169 /// # let qr: QrVerification = unimplemented!();
170 /// # let user_confirmed = false;
171 /// let mut stream = qr.changes();
172 ///
173 /// while let Some(state) = stream.next().await {
174 /// match state {
175 /// QrVerificationState::Scanned => {
176 /// println!("Was the QR code successfully scanned?");
177 ///
178 /// // Ask the user to confirm or cancel here.
179 /// if user_confirmed {
180 /// qr.confirm().await?;
181 /// } else {
182 /// qr.cancel().await?;
183 /// }
184 /// }
185 /// QrVerificationState::Done { .. } => {
186 /// let device = qr.other_device();
187 ///
188 /// println!(
189 /// "Successfully verified device {} {} {:?}",
190 /// device.user_id(),
191 /// device.device_id(),
192 /// device.local_trust_state()
193 /// );
194 ///
195 /// break;
196 /// }
197 /// QrVerificationState::Cancelled(cancel_info) => {
198 /// println!(
199 /// "The verification has been cancelled, reason: {}",
200 /// cancel_info.reason()
201 /// );
202 /// break;
203 /// }
204 /// QrVerificationState::Started
205 /// | QrVerificationState::Reciprocated
206 /// | QrVerificationState::Confirmed => (),
207 /// }
208 /// }
209 /// # anyhow::Ok(()) };
210 /// ```
211 pub fn changes(&self) -> impl Stream<Item = QrVerificationState> + use<> {
212 self.inner.changes()
213 }
214
215 /// Get the current state the verification process is in.
216 ///
217 /// To listen to changes to the [`QrVerificationState`] use the
218 /// [`QrVerification::changes`] method.
219 pub fn state(&self) -> QrVerificationState {
220 self.inner.state()
221 }
222
223 /// Get the room ID, if the verification is happening inside a room.
224 pub fn room_id(&self) -> Option<&RoomId> {
225 self.inner.room_id()
226 }
227}