1use std::sync::{Arc, RwLock};
2
3use futures_util::StreamExt;
4use matrix_sdk::{
5 encryption::{
6 identities::UserIdentity,
7 verification::{SasState, SasVerification, VerificationRequest, VerificationRequestState},
8 Encryption,
9 },
10 ruma::events::key::verification::VerificationMethod,
11 Account,
12};
13use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
14use ruma::UserId;
15use tracing::{error, warn};
16
17use crate::{
18 client::UserProfile, error::ClientError, runtime::get_runtime_handle, utils::Timestamp,
19};
20
21#[derive(uniffi::Object)]
22pub struct SessionVerificationEmoji {
23 symbol: String,
24 description: String,
25}
26
27#[matrix_sdk_ffi_macros::export]
28impl SessionVerificationEmoji {
29 pub fn symbol(&self) -> String {
30 self.symbol.clone()
31 }
32
33 pub fn description(&self) -> String {
34 self.description.clone()
35 }
36}
37
38#[derive(uniffi::Enum)]
39pub enum SessionVerificationData {
40 Emojis { emojis: Vec<Arc<SessionVerificationEmoji>>, indices: Vec<u8> },
41 Decimals { values: Vec<u16> },
42}
43
44#[derive(uniffi::Record)]
46pub struct SessionVerificationRequestDetails {
47 sender_profile: UserProfile,
48 flow_id: String,
49 device_id: String,
50 device_display_name: Option<String>,
51 first_seen_timestamp: Timestamp,
53}
54
55#[matrix_sdk_ffi_macros::export(callback_interface)]
56pub trait SessionVerificationControllerDelegate: SyncOutsideWasm + SendOutsideWasm {
57 fn did_receive_verification_request(&self, details: SessionVerificationRequestDetails);
58 fn did_accept_verification_request(&self);
59 fn did_start_sas_verification(&self);
60 fn did_receive_verification_data(&self, data: SessionVerificationData);
61 fn did_fail(&self);
62 fn did_cancel(&self);
63 fn did_finish(&self);
64}
65
66pub type Delegate = Arc<RwLock<Option<Box<dyn SessionVerificationControllerDelegate>>>>;
67
68#[derive(Clone, uniffi::Object)]
69pub struct SessionVerificationController {
70 encryption: Encryption,
71 user_identity: UserIdentity,
72 account: Account,
73 delegate: Delegate,
74 verification_request: Arc<RwLock<Option<VerificationRequest>>>,
75 sas_verification: Arc<RwLock<Option<SasVerification>>>,
76}
77
78#[matrix_sdk_ffi_macros::export]
79impl SessionVerificationController {
80 pub fn set_delegate(&self, delegate: Option<Box<dyn SessionVerificationControllerDelegate>>) {
81 *self.delegate.write().unwrap() = delegate;
82 }
83
84 pub async fn acknowledge_verification_request(
89 &self,
90 sender_id: String,
91 flow_id: String,
92 ) -> Result<(), ClientError> {
93 let sender_id = UserId::parse(sender_id.clone())?;
94
95 let verification_request = self
96 .encryption
97 .get_verification_request(&sender_id, flow_id)
98 .await
99 .ok_or(ClientError::from_str("Unknown session verification request", None))?;
100
101 self.set_ongoing_verification_request(verification_request)
102 }
103
104 pub async fn accept_verification_request(&self) -> Result<(), ClientError> {
106 let verification_request = self.verification_request.read().unwrap().clone();
107
108 if let Some(verification_request) = verification_request {
109 let methods = vec![VerificationMethod::SasV1];
110 verification_request.accept_with_methods(methods).await?;
111 }
112
113 Ok(())
114 }
115
116 pub async fn request_device_verification(&self) -> Result<(), ClientError> {
118 let methods = vec![VerificationMethod::SasV1];
119 let verification_request =
120 self.user_identity.request_verification_with_methods(methods).await?;
121
122 self.set_ongoing_verification_request(verification_request)
123 }
124
125 pub async fn request_user_verification(&self, user_id: String) -> Result<(), ClientError> {
127 let user_id = UserId::parse(user_id)?;
128
129 let user_identity = self
130 .encryption
131 .get_user_identity(&user_id)
132 .await?
133 .ok_or(ClientError::from_str("Unknown user identity", None))?;
134
135 if user_identity.is_verified() {
136 return Err(ClientError::from_str("User is already verified", None));
137 }
138
139 let methods = vec![VerificationMethod::SasV1];
140
141 let verification_request = user_identity.request_verification_with_methods(methods).await?;
142
143 self.set_ongoing_verification_request(verification_request)
144 }
145
146 pub async fn start_sas_verification(&self) -> Result<(), ClientError> {
149 let verification_request = self.verification_request.read().unwrap().clone();
150
151 let Some(verification_request) = verification_request else {
152 return Err(ClientError::from_str("Verification request missing.", None));
153 };
154
155 match verification_request.start_sas().await {
156 Ok(Some(verification)) => {
157 *self.sas_verification.write().unwrap() = Some(verification.clone());
158
159 if let Some(delegate) = &*self.delegate.read().unwrap() {
160 delegate.did_start_sas_verification()
161 }
162
163 let delegate = self.delegate.clone();
164 get_runtime_handle()
165 .spawn(Self::listen_to_sas_verification_changes(verification, delegate));
166 }
167 _ => {
168 if let Some(delegate) = &*self.delegate.read().unwrap() {
169 delegate.did_fail()
170 }
171 }
172 }
173
174 Ok(())
175 }
176
177 pub async fn approve_verification(&self) -> Result<(), ClientError> {
179 let sas_verification = self.sas_verification.read().unwrap().clone();
180
181 let Some(sas_verification) = sas_verification else {
182 return Err(ClientError::from_str("SAS verification missing", None));
183 };
184
185 Ok(sas_verification.confirm().await?)
186 }
187
188 pub async fn decline_verification(&self) -> Result<(), ClientError> {
190 let sas_verification = self.sas_verification.read().unwrap().clone();
191
192 let Some(sas_verification) = sas_verification else {
193 return Err(ClientError::from_str("SAS verification missing", None));
194 };
195
196 Ok(sas_verification.mismatch().await?)
197 }
198
199 pub async fn cancel_verification(&self) -> Result<(), ClientError> {
201 let verification_request = self.verification_request.read().unwrap().clone();
202
203 let Some(verification_request) = verification_request else {
204 return Err(ClientError::from_str("Verification request missing.", None));
205 };
206
207 Ok(verification_request.cancel().await?)
208 }
209}
210
211impl SessionVerificationController {
212 pub(crate) fn new(
213 encryption: Encryption,
214 user_identity: UserIdentity,
215 account: Account,
216 ) -> Self {
217 SessionVerificationController {
218 encryption,
219 user_identity,
220 account,
221 delegate: Arc::new(RwLock::new(None)),
222 verification_request: Arc::new(RwLock::new(None)),
223 sas_verification: Arc::new(RwLock::new(None)),
224 }
225 }
226
227 pub(crate) async fn process_incoming_verification_request(
231 &self,
232 sender: &UserId,
233 flow_id: impl AsRef<str>,
234 ) {
235 if sender != self.user_identity.user_id() {
236 if let Some(status) = self.encryption.cross_signing_status().await {
237 if !status.is_complete() {
238 warn!(
239 "Cannot verify other users until our own device's cross-signing status \
240 is complete: {status:?}"
241 );
242 return;
243 }
244 }
245 }
246
247 let Some(request) = self.encryption.get_verification_request(sender, flow_id).await else {
248 error!("Failed retrieving verification request");
249 return;
250 };
251
252 let VerificationRequestState::Requested { other_device_data, .. } = request.state() else {
253 error!("Received verification request event but the request is in the wrong state.");
254 return;
255 };
256
257 let Ok(sender_profile) = UserProfile::fetch(&self.account, sender).await else {
258 error!("Failed fetching user profile for verification request");
259 return;
260 };
261
262 if let Some(delegate) = &*self.delegate.read().unwrap() {
263 delegate.did_receive_verification_request(SessionVerificationRequestDetails {
264 sender_profile,
265 flow_id: request.flow_id().into(),
266 device_id: other_device_data.device_id().into(),
267 device_display_name: other_device_data.display_name().map(str::to_string),
268 first_seen_timestamp: other_device_data.first_time_seen_ts().into(),
269 });
270 }
271 }
272
273 fn set_ongoing_verification_request(
274 &self,
275 verification_request: VerificationRequest,
276 ) -> Result<(), ClientError> {
277 if let Some(ongoing_verification_request) =
278 self.verification_request.read().unwrap().clone()
279 {
280 if !ongoing_verification_request.is_done()
281 && !ongoing_verification_request.is_cancelled()
282 {
283 return Err(ClientError::from_str(
284 "There is another verification flow ongoing.",
285 None,
286 ));
287 }
288 }
289
290 *self.verification_request.write().unwrap() = Some(verification_request.clone());
291
292 get_runtime_handle().spawn(Self::listen_to_verification_request_changes(
293 verification_request,
294 self.sas_verification.clone(),
295 self.delegate.clone(),
296 ));
297
298 Ok(())
299 }
300
301 async fn listen_to_verification_request_changes(
302 verification_request: VerificationRequest,
303 sas_verification: Arc<RwLock<Option<SasVerification>>>,
304 delegate: Delegate,
305 ) {
306 let mut stream = verification_request.changes();
307
308 while let Some(state) = stream.next().await {
309 match state {
310 VerificationRequestState::Transitioned { verification } => {
311 let Some(verification) = verification.sas() else {
312 error!("Invalid, non-sas verification flow. Returning.");
313 return;
314 };
315
316 *sas_verification.write().unwrap() = Some(verification.clone());
317
318 if verification.accept().await.is_ok() {
319 if let Some(delegate) = &*delegate.read().unwrap() {
320 delegate.did_start_sas_verification()
321 }
322
323 let delegate = delegate.clone();
324 get_runtime_handle().spawn(Self::listen_to_sas_verification_changes(
325 verification,
326 delegate,
327 ));
328 } else if let Some(delegate) = &*delegate.read().unwrap() {
329 delegate.did_fail()
330 }
331 }
332 VerificationRequestState::Ready { .. } => {
333 if let Some(delegate) = &*delegate.read().unwrap() {
334 delegate.did_accept_verification_request()
335 }
336 }
337 VerificationRequestState::Cancelled(..) => {
338 if let Some(delegate) = &*delegate.read().unwrap() {
339 delegate.did_cancel();
340 }
341 }
342 _ => {}
343 }
344 }
345 }
346
347 async fn listen_to_sas_verification_changes(sas: SasVerification, delegate: Delegate) {
348 let mut stream = sas.changes();
349
350 while let Some(state) = stream.next().await {
351 match state {
352 SasState::KeysExchanged { emojis, decimals } => {
353 if let Some(delegate) = &*delegate.read().unwrap() {
354 if let Some(emojis) = emojis {
355 delegate.did_receive_verification_data(
356 SessionVerificationData::Emojis {
357 emojis: emojis
358 .emojis
359 .into_iter()
360 .map(|emoji| {
361 Arc::new(SessionVerificationEmoji {
362 symbol: emoji.symbol.to_owned(),
363 description: emoji.description.to_owned(),
364 })
365 })
366 .collect(),
367 indices: emojis.indices.to_vec(),
368 },
369 );
370 } else {
371 delegate.did_receive_verification_data(
372 SessionVerificationData::Decimals {
373 values: vec![decimals.0, decimals.1, decimals.2],
374 },
375 )
376 }
377 }
378 }
379 SasState::Done { .. } => {
380 if let Some(delegate) = &*delegate.read().unwrap() {
381 delegate.did_finish()
382 }
383 break;
384 }
385 SasState::Cancelled(_cancel_info) => {
386 if let Some(delegate) = &*delegate.read().unwrap() {
389 delegate.did_cancel()
390 }
391 break;
392 }
393 SasState::Created { .. }
394 | SasState::Started { .. }
395 | SasState::Accepted { .. }
396 | SasState::Confirmed => (),
397 }
398 }
399 }
400}