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 matrix_sdk_qrcode::{qrcode::QrCode, EncodingError},
18 CancelInfo, DeviceData, QrVerification as BaseQrVerification, QrVerificationState,
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: 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
122 /// each step of the verification process.
123 ///
124 /// # Flowchart
125 ///
126 /// The flow of the verification process is pictured below. Please note
127 /// that the process can be cancelled at each step of the process.
128 /// Either side 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 /// # Examples
160 ///
161 /// ```no_run
162 /// use futures_util::{Stream, StreamExt};
163 /// use matrix_sdk::encryption::verification::{
164 /// QrVerification, QrVerificationState,
165 /// };
166 ///
167 /// # async {
168 /// # let qr: QrVerification = unimplemented!();
169 /// # let user_confirmed = false;
170 /// let mut stream = qr.changes();
171 ///
172 /// while let Some(state) = stream.next().await {
173 /// match state {
174 /// QrVerificationState::Scanned => {
175 /// println!("Was the QR code successfully scanned?");
176 ///
177 /// // Ask the user to confirm or cancel here.
178 /// if user_confirmed {
179 /// qr.confirm().await?;
180 /// } else {
181 /// qr.cancel().await?;
182 /// }
183 /// }
184 /// QrVerificationState::Done { .. } => {
185 /// let device = qr.other_device();
186 ///
187 /// println!(
188 /// "Successfully verified device {} {} {:?}",
189 /// device.user_id(),
190 /// device.device_id(),
191 /// device.local_trust_state()
192 /// );
193 ///
194 /// break;
195 /// }
196 /// QrVerificationState::Cancelled(cancel_info) => {
197 /// println!(
198 /// "The verification has been cancelled, reason: {}",
199 /// cancel_info.reason()
200 /// );
201 /// break;
202 /// }
203 /// QrVerificationState::Started
204 /// | QrVerificationState::Reciprocated
205 /// | QrVerificationState::Confirmed => (),
206 /// }
207 /// }
208 /// # anyhow::Ok(()) };
209 /// ```
210 pub fn changes(&self) -> impl Stream<Item = QrVerificationState> {
211 self.inner.changes()
212 }
213
214 /// Get the current state the verification process is in.
215 ///
216 /// To listen to changes to the [`QrVerificationState`] use the
217 /// [`QrVerification::changes`] method.
218 pub fn state(&self) -> QrVerificationState {
219 self.inner.state()
220 }
221
222 /// Get the room ID, if the verification is happening inside a room.
223 pub fn room_id(&self) -> Option<&RoomId> {
224 self.inner.room_id()
225 }
226}