1use std::sync::Arc;
2
3use futures_util::StreamExt;
4use matrix_sdk::{
5 encryption,
6 encryption::{backups, recovery},
7};
8use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
9use thiserror::Error;
10use tracing::{error, info};
11use zeroize::Zeroize;
12
13use crate::{
14 client::Client, error::ClientError, ruma::AuthData, runtime::get_runtime_handle,
15 task_handle::TaskHandle,
16};
17
18#[derive(uniffi::Object)]
19pub struct Encryption {
20 pub(crate) inner: matrix_sdk::encryption::Encryption,
21
22 pub(crate) _client: Arc<Client>,
28}
29
30#[matrix_sdk_ffi_macros::export(callback_interface)]
31pub trait BackupStateListener: SyncOutsideWasm + SendOutsideWasm {
32 fn on_update(&self, status: BackupState);
33}
34
35#[matrix_sdk_ffi_macros::export(callback_interface)]
36pub trait BackupSteadyStateListener: SyncOutsideWasm + SendOutsideWasm {
37 fn on_update(&self, status: BackupUploadState);
38}
39
40#[matrix_sdk_ffi_macros::export(callback_interface)]
41pub trait RecoveryStateListener: SyncOutsideWasm + SendOutsideWasm {
42 fn on_update(&self, status: RecoveryState);
43}
44
45#[matrix_sdk_ffi_macros::export(callback_interface)]
46pub trait VerificationStateListener: SyncOutsideWasm + SendOutsideWasm {
47 fn on_update(&self, status: VerificationState);
48}
49
50#[derive(uniffi::Enum)]
51pub enum BackupUploadState {
52 Waiting,
53 Uploading { backed_up_count: u32, total_count: u32 },
54 Error,
55 Done,
56}
57
58#[derive(Debug, Error, uniffi::Error)]
59#[uniffi(flat_error)]
60pub enum SteadyStateError {
61 #[error("The backup got disabled while waiting for the room keys to be uploaded.")]
62 BackupDisabled,
63 #[error("There was a connection error.")]
64 Connection,
65 #[error("We couldn't read status updates from the upload task quickly enough.")]
66 Lagged,
67}
68
69#[derive(Debug, Error, uniffi::Error)]
70pub enum RecoveryError {
71 #[error(
74 "A backup already exists on the homeserver and the method does not allow to overwrite it"
75 )]
76 BackupExistsOnServer,
77
78 #[error(transparent)]
80 Client { source: crate::ClientError },
81
82 #[error("Error in the secret-storage subsystem: {error_message}")]
84 SecretStorage { error_message: String },
85}
86
87impl From<matrix_sdk::encryption::recovery::RecoveryError> for RecoveryError {
88 fn from(value: matrix_sdk::encryption::recovery::RecoveryError) -> Self {
89 match value {
90 recovery::RecoveryError::BackupExistsOnServer => Self::BackupExistsOnServer,
91 recovery::RecoveryError::Sdk(e) => Self::Client { source: ClientError::from(e) },
92 recovery::RecoveryError::SecretStorage(e) => {
93 Self::SecretStorage { error_message: e.to_string() }
94 }
95 }
96 }
97}
98
99pub type Result<A, E = RecoveryError> = std::result::Result<A, E>;
100
101impl From<matrix_sdk::encryption::backups::futures::SteadyStateError> for SteadyStateError {
102 fn from(value: matrix_sdk::encryption::backups::futures::SteadyStateError) -> Self {
103 match value {
104 backups::futures::SteadyStateError::BackupDisabled => Self::BackupDisabled,
105 backups::futures::SteadyStateError::Connection => Self::Connection,
106 backups::futures::SteadyStateError::Lagged => Self::Lagged,
107 }
108 }
109}
110
111#[derive(uniffi::Enum)]
112pub enum BackupState {
113 Unknown,
114 Creating,
115 Enabling,
116 Resuming,
117 Enabled,
118 Downloading,
119 Disabling,
120}
121
122impl From<backups::BackupState> for BackupState {
123 fn from(value: backups::BackupState) -> Self {
124 match value {
125 backups::BackupState::Unknown => Self::Unknown,
126 backups::BackupState::Creating => Self::Creating,
127 backups::BackupState::Enabling => Self::Enabling,
128 backups::BackupState::Resuming => Self::Resuming,
129 backups::BackupState::Enabled => Self::Enabled,
130 backups::BackupState::Downloading => Self::Downloading,
131 backups::BackupState::Disabling => Self::Disabling,
132 }
133 }
134}
135
136impl From<backups::UploadState> for BackupUploadState {
137 fn from(value: backups::UploadState) -> Self {
138 match value {
139 backups::UploadState::Idle => Self::Waiting,
140 backups::UploadState::Uploading(count) => Self::Uploading {
141 backed_up_count: count.backed_up.try_into().unwrap_or(u32::MAX),
142 total_count: count.total.try_into().unwrap_or(u32::MAX),
143 },
144 backups::UploadState::Error => Self::Error,
145 backups::UploadState::Done => Self::Done,
146 }
147 }
148}
149
150#[derive(uniffi::Enum)]
151pub enum RecoveryState {
152 Unknown,
153 Enabled,
154 Disabled,
155 Incomplete,
156}
157
158impl From<recovery::RecoveryState> for RecoveryState {
159 fn from(value: recovery::RecoveryState) -> Self {
160 match value {
161 recovery::RecoveryState::Unknown => Self::Unknown,
162 recovery::RecoveryState::Enabled => Self::Enabled,
163 recovery::RecoveryState::Disabled => Self::Disabled,
164 recovery::RecoveryState::Incomplete => Self::Incomplete,
165 }
166 }
167}
168
169#[matrix_sdk_ffi_macros::export(callback_interface)]
170pub trait EnableRecoveryProgressListener: SyncOutsideWasm + SendOutsideWasm {
171 fn on_update(&self, status: EnableRecoveryProgress);
172}
173
174#[derive(uniffi::Enum)]
175pub enum EnableRecoveryProgress {
176 Starting,
177 CreatingBackup,
178 CreatingRecoveryKey,
179 BackingUp { backed_up_count: u32, total_count: u32 },
180 RoomKeyUploadError,
181 Done { recovery_key: String },
182}
183
184impl From<recovery::EnableProgress> for EnableRecoveryProgress {
185 fn from(value: recovery::EnableProgress) -> Self {
186 match &value {
187 recovery::EnableProgress::Starting => Self::Starting,
188 recovery::EnableProgress::CreatingBackup => Self::CreatingBackup,
189 recovery::EnableProgress::CreatingRecoveryKey => Self::CreatingRecoveryKey,
190 recovery::EnableProgress::BackingUp(counts) => Self::BackingUp {
191 backed_up_count: counts.backed_up.try_into().unwrap_or(u32::MAX),
192 total_count: counts.backed_up.try_into().unwrap_or(u32::MAX),
193 },
194 recovery::EnableProgress::RoomKeyUploadError => Self::RoomKeyUploadError,
195 recovery::EnableProgress::Done { recovery_key } => {
196 Self::Done { recovery_key: recovery_key.to_owned() }
197 }
198 }
199 }
200}
201
202#[derive(uniffi::Enum)]
203pub enum VerificationState {
204 Unknown,
205 Verified,
206 Unverified,
207}
208
209impl From<encryption::VerificationState> for VerificationState {
210 fn from(value: encryption::VerificationState) -> Self {
211 match &value {
212 encryption::VerificationState::Unknown => Self::Unknown,
213 encryption::VerificationState::Verified => Self::Verified,
214 encryption::VerificationState::Unverified => Self::Unverified,
215 }
216 }
217}
218
219#[matrix_sdk_ffi_macros::export]
220impl Encryption {
221 pub async fn ed25519_key(&self) -> Option<String> {
224 self.inner.ed25519_key().await
225 }
226
227 pub async fn curve25519_key(&self) -> Option<String> {
230 self.inner.curve25519_key().await.map(|k| k.to_base64())
231 }
232
233 pub fn backup_state_listener(&self, listener: Box<dyn BackupStateListener>) -> Arc<TaskHandle> {
234 let mut stream = self.inner.backups().state_stream();
235
236 let stream_task = TaskHandle::new(get_runtime_handle().spawn(async move {
237 while let Some(state) = stream.next().await {
238 let Ok(state) = state else { continue };
239 listener.on_update(state.into());
240 }
241 }));
242
243 stream_task.into()
244 }
245
246 pub fn backup_state(&self) -> BackupState {
247 self.inner.backups().state().into()
248 }
249
250 pub async fn backup_exists_on_server(&self) -> Result<bool, ClientError> {
260 Ok(self.inner.backups().fetch_exists_on_server().await?)
261 }
262
263 pub fn recovery_state(&self) -> RecoveryState {
264 self.inner.recovery().state().into()
265 }
266
267 pub fn recovery_state_listener(
268 &self,
269 listener: Box<dyn RecoveryStateListener>,
270 ) -> Arc<TaskHandle> {
271 let mut stream = self.inner.recovery().state_stream();
272
273 let stream_task = TaskHandle::new(get_runtime_handle().spawn(async move {
274 while let Some(state) = stream.next().await {
275 listener.on_update(state.into());
276 }
277 }));
278
279 stream_task.into()
280 }
281
282 pub async fn enable_backups(&self) -> Result<()> {
283 Ok(self.inner.recovery().enable_backup().await?)
284 }
285
286 pub async fn is_last_device(&self) -> Result<bool> {
287 Ok(self.inner.recovery().is_last_device().await?)
288 }
289
290 pub async fn wait_for_backup_upload_steady_state(
291 &self,
292 progress_listener: Option<Box<dyn BackupSteadyStateListener>>,
293 ) -> Result<(), SteadyStateError> {
294 let backups = self.inner.backups();
295 let wait_for_steady_state = backups.wait_for_steady_state();
296
297 let task = if let Some(listener) = progress_listener {
298 let mut progress_stream = wait_for_steady_state.subscribe_to_progress();
299
300 Some(get_runtime_handle().spawn(async move {
301 while let Some(progress) = progress_stream.next().await {
302 let Ok(progress) = progress else { continue };
303 listener.on_update(progress.into());
304 }
305 }))
306 } else {
307 None
308 };
309
310 let result = wait_for_steady_state.await;
311
312 if let Some(task) = task {
313 task.abort();
314 }
315
316 Ok(result?)
317 }
318
319 pub async fn enable_recovery(
320 &self,
321 wait_for_backups_to_upload: bool,
322 mut passphrase: Option<String>,
323 progress_listener: Box<dyn EnableRecoveryProgressListener>,
324 ) -> Result<String> {
325 let recovery = self.inner.recovery();
326
327 let enable = if wait_for_backups_to_upload {
328 recovery.enable().wait_for_backups_to_upload()
329 } else {
330 recovery.enable()
331 };
332
333 let enable = if let Some(passphrase) = &passphrase {
334 enable.with_passphrase(passphrase)
335 } else {
336 enable
337 };
338
339 let mut progress_stream = enable.subscribe_to_progress();
340
341 let task = get_runtime_handle().spawn(async move {
342 while let Some(progress) = progress_stream.next().await {
343 let Ok(progress) = progress else { continue };
344 progress_listener.on_update(progress.into());
345 }
346 });
347
348 let ret = enable.await?;
349
350 task.abort();
351 passphrase.zeroize();
352
353 Ok(ret)
354 }
355
356 pub async fn disable_recovery(&self) -> Result<()> {
357 Ok(self.inner.recovery().disable().await?)
358 }
359
360 pub async fn reset_recovery_key(&self) -> Result<String> {
361 Ok(self.inner.recovery().reset_key().await?)
362 }
363
364 pub async fn recover_and_reset(&self, mut old_recovery_key: String) -> Result<String> {
365 let result = self.inner.recovery().recover_and_reset(&old_recovery_key).await;
366
367 old_recovery_key.zeroize();
368
369 Ok(result?)
370 }
371
372 pub async fn reset_identity(&self) -> Result<Option<Arc<IdentityResetHandle>>, ClientError> {
375 if let Some(reset_handle) =
376 self.inner.recovery().reset_identity().await.map_err(ClientError::from_err)?
377 {
378 return Ok(Some(Arc::new(IdentityResetHandle { inner: reset_handle })));
379 }
380
381 Ok(None)
382 }
383
384 pub async fn recover(&self, mut recovery_key: String) -> Result<()> {
385 let result = self.inner.recovery().recover(&recovery_key).await;
386
387 recovery_key.zeroize();
388
389 Ok(result?)
390 }
391
392 pub fn verification_state(&self) -> VerificationState {
393 self.inner.verification_state().get().into()
394 }
395
396 pub fn verification_state_listener(
397 self: Arc<Self>,
398 listener: Box<dyn VerificationStateListener>,
399 ) -> Arc<TaskHandle> {
400 let mut subscriber = self.inner.verification_state();
401
402 Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
403 while let Some(verification_state) = subscriber.next().await {
404 listener.on_update(verification_state.into());
405 }
406 })))
407 }
408
409 pub async fn wait_for_e2ee_initialization_tasks(&self) {
412 self.inner.wait_for_e2ee_initialization_tasks().await;
413 }
414
415 pub async fn user_identity(
432 &self,
433 user_id: String,
434 ) -> Result<Option<Arc<UserIdentity>>, ClientError> {
435 match self.inner.get_user_identity(user_id.as_str().try_into()?).await {
436 Ok(Some(identity)) => {
437 return Ok(Some(Arc::new(UserIdentity { inner: identity })));
438 }
439 Ok(None) => {
440 info!("No identity found in the store.");
441 }
442 Err(error) => {
443 error!("Failed fetching identity from the store: {error}");
444 }
445 }
446
447 info!("Requesting identity from the server.");
448
449 let identity = self.inner.request_user_identity(user_id.as_str().try_into()?).await?;
450 Ok(identity.map(|identity| Arc::new(UserIdentity { inner: identity })))
451 }
452}
453
454#[derive(uniffi::Object)]
456pub struct UserIdentity {
457 inner: matrix_sdk::encryption::identities::UserIdentity,
458}
459
460#[matrix_sdk_ffi_macros::export]
461impl UserIdentity {
462 pub(crate) async fn pin(&self) -> Result<(), ClientError> {
477 Ok(self.inner.pin().await?)
478 }
479
480 pub(crate) fn master_key(&self) -> Option<String> {
487 self.inner.master_key().get_first_key().map(|k| k.to_base64())
488 }
489
490 pub fn is_verified(&self) -> bool {
495 self.inner.is_verified()
496 }
497
498 pub fn was_previously_verified(&self) -> bool {
503 self.inner.was_previously_verified()
504 }
505
506 pub(crate) async fn withdraw_verification(&self) -> Result<(), ClientError> {
512 Ok(self.inner.withdraw_verification().await?)
513 }
514
515 pub fn has_verification_violation(&self) -> bool {
517 self.inner.has_verification_violation()
518 }
519}
520
521#[derive(uniffi::Object)]
522pub struct IdentityResetHandle {
523 pub(crate) inner: matrix_sdk::encryption::recovery::IdentityResetHandle,
524}
525
526#[matrix_sdk_ffi_macros::export]
527impl IdentityResetHandle {
528 pub fn auth_type(&self) -> CrossSigningResetAuthType {
531 self.inner.auth_type().into()
532 }
533
534 pub async fn reset(&self, auth: Option<AuthData>) -> Result<(), ClientError> {
542 if let Some(auth) = auth {
543 self.inner.reset(Some(auth.into())).await.map_err(ClientError::from_err)
544 } else {
545 self.inner.reset(None).await.map_err(ClientError::from_err)
546 }
547 }
548
549 pub async fn cancel(&self) {
550 self.inner.cancel().await;
551 }
552}
553
554#[derive(uniffi::Enum)]
555pub enum CrossSigningResetAuthType {
556 Uiaa,
558 Oidc {
561 info: OidcCrossSigningResetInfo,
562 },
563}
564
565impl From<&matrix_sdk::encryption::CrossSigningResetAuthType> for CrossSigningResetAuthType {
566 fn from(value: &matrix_sdk::encryption::CrossSigningResetAuthType) -> Self {
567 match value {
568 encryption::CrossSigningResetAuthType::Uiaa(_) => Self::Uiaa,
569 encryption::CrossSigningResetAuthType::OAuth(info) => Self::Oidc { info: info.into() },
570 }
571 }
572}
573
574#[derive(uniffi::Record)]
575pub struct OidcCrossSigningResetInfo {
576 pub approval_url: String,
578}
579
580impl From<&matrix_sdk::encryption::OAuthCrossSigningResetInfo> for OidcCrossSigningResetInfo {
581 fn from(value: &matrix_sdk::encryption::OAuthCrossSigningResetInfo) -> Self {
582 Self { approval_url: value.approval_url.to_string() }
583 }
584}