atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: bindings/matrix-sdk-ffi/src/encryption.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2025 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for that specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
use std::{str::FromStr, sync::Arc};
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
use futures_util::StreamExt;
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
use matrix_sdk::encryption::{self, backups, recovery};
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
use matrix_sdk_base::crypto::types::{BackupSecrets, RoomKeyBackupInfo};
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
use ruma::OwnedUserId;
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
use serde::de::Error;
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
use thiserror::Error;
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
use tracing::{error, info};
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
use zeroize::Zeroize;
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
    client::Client, error::ClientError, ruma::AuthData, runtime::get_runtime_handle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
    task_handle::TaskHandle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
pub struct Encryption {
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
    pub(crate) inner: matrix_sdk::encryption::Encryption,
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
    /// A reference to the FFI client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
    /// Note: we do this to make it so that the FFI `NotificationClient` keeps
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
    /// the FFI `Client` and thus the SDK `Client` alive. Otherwise, we
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
    /// would need to repeat the hack done in the FFI `Client::drop` method.
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
    pub(crate) _client: Arc<Client>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
pub trait BackupStateListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
    fn on_update(&self, status: BackupState);
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
pub trait BackupSteadyStateListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
    fn on_update(&self, status: BackupUploadState);
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
pub trait RecoveryStateListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    fn on_update(&self, status: RecoveryState);
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
pub trait VerificationStateListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
    fn on_update(&self, status: VerificationState);
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
pub enum BackupUploadState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
    Waiting,
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    Uploading { backed_up_count: u32, total_count: u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
    Error,
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
    Done,
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
#[derive(Debug, Error, uniffi::Error)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
#[uniffi(flat_error)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
pub enum SteadyStateError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
    #[error("The backup got disabled while waiting for the room keys to be uploaded.")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
    BackupDisabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
    #[error("There was a connection error.")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
    Connection,
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
    #[error("We couldn't read status updates from the upload task quickly enough.")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
    Lagged,
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
#[derive(Debug, Error, uniffi::Error)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
pub enum RecoveryError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
    /// A backup already exists on the homeserver, the recovery subsystem does
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
    /// not allow backups to be overwritten, disable recovery first.
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
    #[error(
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
        "A backup already exists on the homeserver and the method does not allow to overwrite it"
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
    )]
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
    BackupExistsOnServer,
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
    /// A typical SDK error.
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
    #[error(transparent)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
    Client { source: crate::ClientError },
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
    /// Error in the secret storage subsystem, except for when importing a
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    /// secret.
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
    #[error("Error in the secret-storage subsystem: {error_message}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
    SecretStorage { error_message: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
    /// Error when importing a secret from secret storage.
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
    #[error("Error importing a secret: {error_message}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
    Import { error_message: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
impl From<matrix_sdk::encryption::recovery::RecoveryError> for RecoveryError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
    fn from(value: matrix_sdk::encryption::recovery::RecoveryError) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
            recovery::RecoveryError::BackupExistsOnServer => Self::BackupExistsOnServer,
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
            recovery::RecoveryError::Sdk(e) => Self::Client { source: ClientError::from(e) },
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
            recovery::RecoveryError::SecretStorage(
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
                matrix_sdk::encryption::secret_storage::SecretStorageError::ImportError { .. },
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
            ) => Self::Import { error_message: value.to_string() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
            recovery::RecoveryError::SecretStorage(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
                Self::SecretStorage { error_message: e.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
pub type Result<A, E = RecoveryError> = std::result::Result<A, E>;
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
impl From<matrix_sdk::encryption::backups::futures::SteadyStateError> for SteadyStateError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
    fn from(value: matrix_sdk::encryption::backups::futures::SteadyStateError) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
            backups::futures::SteadyStateError::BackupDisabled => Self::BackupDisabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
            backups::futures::SteadyStateError::Connection => Self::Connection,
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
            backups::futures::SteadyStateError::Lagged => Self::Lagged,
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
pub enum BackupState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
    Unknown,
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    Creating,
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
    Enabling,
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
    Resuming,
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
    Enabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
    Downloading,
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
    Disabling,
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
impl From<backups::BackupState> for BackupState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
    fn from(value: backups::BackupState) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
            backups::BackupState::Unknown => Self::Unknown,
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
            backups::BackupState::Creating => Self::Creating,
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
            backups::BackupState::Enabling => Self::Enabling,
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
            backups::BackupState::Resuming => Self::Resuming,
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
            backups::BackupState::Enabled => Self::Enabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
            backups::BackupState::Downloading => Self::Downloading,
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
            backups::BackupState::Disabling => Self::Disabling,
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
impl From<backups::UploadState> for BackupUploadState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
    fn from(value: backups::UploadState) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
            backups::UploadState::Idle => Self::Waiting,
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
            backups::UploadState::Uploading(count) => Self::Uploading {
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
                backed_up_count: count.backed_up.try_into().unwrap_or(u32::MAX),
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
                total_count: count.total.try_into().unwrap_or(u32::MAX),
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
            backups::UploadState::Error => Self::Error,
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
            backups::UploadState::Done => Self::Done,
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
pub enum RecoveryState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
    Unknown,
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    Enabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    Incomplete,
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
impl From<recovery::RecoveryState> for RecoveryState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
    fn from(value: recovery::RecoveryState) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
            recovery::RecoveryState::Unknown => Self::Unknown,
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
            recovery::RecoveryState::Enabled => Self::Enabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
            recovery::RecoveryState::Disabled => Self::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
            recovery::RecoveryState::Incomplete => Self::Incomplete,
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
pub trait EnableRecoveryProgressListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
    fn on_update(&self, status: EnableRecoveryProgress);
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
pub enum EnableRecoveryProgress {
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
    Starting,
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
    CreatingBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
    CreatingRecoveryKey,
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
    BackingUp { backed_up_count: u32, total_count: u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
    RoomKeyUploadError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
    Done { recovery_key: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
impl From<recovery::EnableProgress> for EnableRecoveryProgress {
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
    fn from(value: recovery::EnableProgress) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
        match &value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
            recovery::EnableProgress::Starting => Self::Starting,
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
            recovery::EnableProgress::CreatingBackup => Self::CreatingBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
            recovery::EnableProgress::CreatingRecoveryKey => Self::CreatingRecoveryKey,
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
            recovery::EnableProgress::BackingUp(counts) => Self::BackingUp {
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
                backed_up_count: counts.backed_up.try_into().unwrap_or(u32::MAX),
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
                total_count: counts.backed_up.try_into().unwrap_or(u32::MAX),
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
            recovery::EnableProgress::RoomKeyUploadError => Self::RoomKeyUploadError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
            recovery::EnableProgress::Done { recovery_key } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
                Self::Done { recovery_key: recovery_key.to_owned() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
pub enum VerificationState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
    Unknown,
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
    Verified,
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
    Unverified,
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
impl From<encryption::VerificationState> for VerificationState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
    fn from(value: encryption::VerificationState) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
        match &value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
            encryption::VerificationState::Unknown => Self::Unknown,
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
            encryption::VerificationState::Verified => Self::Verified,
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
            encryption::VerificationState::Unverified => Self::Unverified,
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
/// Struct containing the bundle of secrets to fully activate a new device for
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
/// end-to-end encryption.
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
pub struct SecretsBundleWithUserId {
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
    user_id: OwnedUserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
    inner: matrix_sdk_base::crypto::types::SecretsBundle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
/// Result for the check if a store has a valid secrets bundle.
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
pub enum DetectedSecretsBundle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
    /// The store doesn't contain a secrets bundle at all.
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
    None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
    /// The store contains a bundle without a backup.
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
    WithoutBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
    /// The store contains a bundle with an unused backup, the backup key in the
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
    /// bundle isn't used on the homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
    UnusedBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
    /// The store contains a complete secrets bundle.
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
    Complete,
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
/// Error type describing failures that can happen while exporting a
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
/// [`SecretsBundle`] from a SQLite store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
#[derive(Debug, thiserror::Error, uniffi::Error)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
pub enum BundleExportError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
    /// The SQLite store couldn't be opened.
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
    #[error("the store couldn't be opened: {msg}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
    OpenStoreError { msg: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
    /// Data from the SQLite store couldn't be exported.
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
    #[error("the bundle couldn't be exported due to a storage error: {msg}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
    StoreError { msg: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
    /// The store doesn't contain a secrets bundle or it couldn't be read from
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
    /// the store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
    #[error("the bundle couldn't be exported: {msg}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
    SecretError { msg: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
    /// The store is empty and doesn't contain a secrets bundle.
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
    #[error("the store is completely empty")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
    StoreEmpty,
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
    /// A JSON object couldn't be deserialized while the secrets bundle was
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
    /// exported.
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
    #[error("Couldn't deserialize a JSON value: {msg}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
    Json { msg: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
    /// Error returned when the secrets bundle is missing a backup key or
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
    /// includes one that doesn’t match the key configured for the active backup
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
    /// version.
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
    #[error(
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
        "The bundle is missing a backup key or has one that isn't the one that's currently used"
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
    )]
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
    InvalidBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
#[cfg(feature = "sqlite")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
impl From<matrix_sdk::encryption::BundleExportError> for BundleExportError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
    fn from(value: matrix_sdk::encryption::BundleExportError) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
            matrix_sdk::encryption::BundleExportError::OpenStoreError(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
                BundleExportError::OpenStoreError { msg: e.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
            matrix_sdk::encryption::BundleExportError::StoreError(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
                BundleExportError::StoreError { msg: e.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
            matrix_sdk::encryption::BundleExportError::SecretExport(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
                BundleExportError::SecretError { msg: e.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
impl From<serde_json::Error> for BundleExportError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
    fn from(value: serde_json::Error) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
        Self::Json { msg: value.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
#[cfg(feature = "sqlite")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
impl SecretsBundleWithUserId {
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
    /// Attempt to export a [`SecretsBundle`] from a crypto store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
    /// This method can be used to retrieve a [`SecretsBundle`] from an existing
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
    /// `matrix-sdk`-based client in order to import the [`SecretsBundle`] in
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
    /// another [`Client`] instance.
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
    /// This can be useful for migration purposes or to allow existing client
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
    /// instances create new ones that will be fully verified.
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
    #[uniffi::constructor]
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
    pub async fn from_database(
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
        database_path: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
        mut passphrase: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
        backup_info: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
    ) -> Result<Arc<Self>, BundleExportError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
        let backup_info = serde_json::from_str(backup_info)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
        let ret = if let Some((user_id, bundle)) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
            matrix_sdk::encryption::export_secrets_bundle_from_store(
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
                database_path,
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
                passphrase.as_deref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
            let is_backup_ok =
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
                bundle.backup.as_ref().is_some_and(|backup| is_valid_backup(backup, &backup_info));
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
            if is_backup_ok {
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
                Ok(SecretsBundleWithUserId { user_id, inner: bundle }.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
            } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
                Err(BundleExportError::InvalidBackup)
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
            Err(BundleExportError::StoreEmpty)
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
        passphrase.zeroize();
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
        ret
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
impl SecretsBundleWithUserId {
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
    /// Attempt to create a [`SecretsBundle`] from a previously JSON serialized
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
    /// bundle.
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
    #[uniffi::constructor]
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
    pub fn from_str(
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
        user_id: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
        bundle: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
        backup_info: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
    ) -> Result<Arc<Self>, BundleExportError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
        let user_id =
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
            OwnedUserId::from_str(user_id).map_err(|e| serde_json::Error::custom(e.to_string()))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
        let bundle: matrix_sdk_base::crypto::types::SecretsBundle = serde_json::from_str(bundle)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
        let backup_info = serde_json::from_str(backup_info)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC374
7ff8adafe9e4atagen2026-10-22 12:30 UTC375
        let is_backup_ok =
7ff8adafe9e4atagen2026-10-22 12:30 UTC376
            bundle.backup.as_ref().is_some_and(|backup| is_valid_backup(backup, &backup_info));
7ff8adafe9e4atagen2026-10-22 12:30 UTC377
7ff8adafe9e4atagen2026-10-22 12:30 UTC378
        if is_backup_ok {
7ff8adafe9e4atagen2026-10-22 12:30 UTC379
            Ok(Self { user_id, inner: bundle }.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC380
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC381
            Err(BundleExportError::InvalidBackup)
7ff8adafe9e4atagen2026-10-22 12:30 UTC382
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC383
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC384
7ff8adafe9e4atagen2026-10-22 12:30 UTC385
    /// Does the bundle contain a backup key.
7ff8adafe9e4atagen2026-10-22 12:30 UTC386
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC387
    /// Since enabling a backup is optional, the backup key might be missing
7ff8adafe9e4atagen2026-10-22 12:30 UTC388
    /// from the bundle. Returns `false` if the backup key is missing,
7ff8adafe9e4atagen2026-10-22 12:30 UTC389
    /// otherwise `true`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC390
    pub fn contains_backup_key(&self) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC391
        self.inner.backup.is_some()
7ff8adafe9e4atagen2026-10-22 12:30 UTC392
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC393
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC394
7ff8adafe9e4atagen2026-10-22 12:30 UTC395
fn is_valid_backup(secrets: &BackupSecrets, info: &RoomKeyBackupInfo) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC396
    match secrets {
7ff8adafe9e4atagen2026-10-22 12:30 UTC397
        BackupSecrets::MegolmBackupV1Curve25519AesSha2(secrets) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC398
            secrets.key.backup_key_matches(info)
7ff8adafe9e4atagen2026-10-22 12:30 UTC399
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC400
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC401
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC402
7ff8adafe9e4atagen2026-10-22 12:30 UTC403
fn check_bundle_and_info(
7ff8adafe9e4atagen2026-10-22 12:30 UTC404
    bundle: &matrix_sdk_base::crypto::types::SecretsBundle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC405
    info: Option<&RoomKeyBackupInfo>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC406
) -> DetectedSecretsBundle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC407
    match (&bundle.backup, info) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC408
        (None, None) => DetectedSecretsBundle::WithoutBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC409
        (None, Some(_)) => DetectedSecretsBundle::WithoutBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC410
        (Some(_), None) => DetectedSecretsBundle::UnusedBackup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC411
        (Some(backup), Some(info)) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC412
            if is_valid_backup(backup, info) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC413
                DetectedSecretsBundle::Complete
7ff8adafe9e4atagen2026-10-22 12:30 UTC414
            } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC415
                DetectedSecretsBundle::UnusedBackup
7ff8adafe9e4atagen2026-10-22 12:30 UTC416
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC417
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC418
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC419
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC420
7ff8adafe9e4atagen2026-10-22 12:30 UTC421
/// Check if a JSON encoded string contains a valid [`SecretsBundle`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC422
#[uniffi::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC423
pub fn json_string_contains_secrets_bundle(
7ff8adafe9e4atagen2026-10-22 12:30 UTC424
    bundle: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC425
    backup_info: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC426
) -> Result<DetectedSecretsBundle, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC427
    let info: Option<RoomKeyBackupInfo> =
7ff8adafe9e4atagen2026-10-22 12:30 UTC428
        backup_info.map(|info| serde_json::from_str(&info)).transpose()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC429
7ff8adafe9e4atagen2026-10-22 12:30 UTC430
    let bundle: matrix_sdk_base::crypto::types::SecretsBundle = serde_json::from_str(bundle)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC431
7ff8adafe9e4atagen2026-10-22 12:30 UTC432
    Ok(check_bundle_and_info(&bundle, info.as_ref()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC433
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC434
7ff8adafe9e4atagen2026-10-22 12:30 UTC435
/// Check if a crypto store contains a valid [`SecretsBundle`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC436
#[cfg(feature = "sqlite")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC437
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC438
pub async fn database_contains_secrets_bundle(
7ff8adafe9e4atagen2026-10-22 12:30 UTC439
    database_path: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC440
    mut passphrase: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC441
    backup_info: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC442
) -> Result<DetectedSecretsBundle, BundleExportError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC443
    let info: Option<RoomKeyBackupInfo> =
7ff8adafe9e4atagen2026-10-22 12:30 UTC444
        backup_info.map(|info| serde_json::from_str(&info)).transpose()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC445
7ff8adafe9e4atagen2026-10-22 12:30 UTC446
    let maybe_bundle = matrix_sdk::encryption::export_secrets_bundle_from_store(
7ff8adafe9e4atagen2026-10-22 12:30 UTC447
        database_path,
7ff8adafe9e4atagen2026-10-22 12:30 UTC448
        passphrase.as_deref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC449
    )
7ff8adafe9e4atagen2026-10-22 12:30 UTC450
    .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC451
7ff8adafe9e4atagen2026-10-22 12:30 UTC452
    passphrase.zeroize();
7ff8adafe9e4atagen2026-10-22 12:30 UTC453
7ff8adafe9e4atagen2026-10-22 12:30 UTC454
    Ok(match maybe_bundle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC455
        Some((_, bundle)) => check_bundle_and_info(&bundle, info.as_ref()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC456
        None => DetectedSecretsBundle::None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC457
    })
7ff8adafe9e4atagen2026-10-22 12:30 UTC458
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC459
7ff8adafe9e4atagen2026-10-22 12:30 UTC460
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC461
impl Encryption {
7ff8adafe9e4atagen2026-10-22 12:30 UTC462
    /// Get the public ed25519 key of our own device. This is usually what is
7ff8adafe9e4atagen2026-10-22 12:30 UTC463
    /// called the fingerprint of the device.
7ff8adafe9e4atagen2026-10-22 12:30 UTC464
    pub async fn ed25519_key(&self) -> Option<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC465
        self.inner.ed25519_key().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC466
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC467
7ff8adafe9e4atagen2026-10-22 12:30 UTC468
    /// Get the public curve25519 key of our own device in base64. This is
7ff8adafe9e4atagen2026-10-22 12:30 UTC469
    /// usually what is called the identity key of the device.
7ff8adafe9e4atagen2026-10-22 12:30 UTC470
    pub async fn curve25519_key(&self) -> Option<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC471
        self.inner.curve25519_key().await.map(|k| k.to_base64())
7ff8adafe9e4atagen2026-10-22 12:30 UTC472
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC473
7ff8adafe9e4atagen2026-10-22 12:30 UTC474
    pub fn backup_state_listener(&self, listener: Box<dyn BackupStateListener>) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC475
        let mut stream = self.inner.backups().state_stream();
7ff8adafe9e4atagen2026-10-22 12:30 UTC476
7ff8adafe9e4atagen2026-10-22 12:30 UTC477
        let stream_task = TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC478
            while let Some(state) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC479
                let Ok(state) = state else { continue };
7ff8adafe9e4atagen2026-10-22 12:30 UTC480
                listener.on_update(state.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC481
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC482
        }));
7ff8adafe9e4atagen2026-10-22 12:30 UTC483
7ff8adafe9e4atagen2026-10-22 12:30 UTC484
        stream_task.into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC485
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC486
7ff8adafe9e4atagen2026-10-22 12:30 UTC487
    pub fn backup_state(&self) -> BackupState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC488
        self.inner.backups().state().into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC489
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC490
7ff8adafe9e4atagen2026-10-22 12:30 UTC491
    /// Does a backup exist on the server?
7ff8adafe9e4atagen2026-10-22 12:30 UTC492
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC493
    /// Because the homeserver doesn't notify us about changes to the backup
7ff8adafe9e4atagen2026-10-22 12:30 UTC494
    /// version, the [`BackupState`] and its listener are a bit crippled.
7ff8adafe9e4atagen2026-10-22 12:30 UTC495
    /// The `BackupState::Unknown` state might mean there is no backup at all or
7ff8adafe9e4atagen2026-10-22 12:30 UTC496
    /// a backup exists but we don't have access to it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC497
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC498
    /// Therefore it is necessary to poll the server for an answer every time
7ff8adafe9e4atagen2026-10-22 12:30 UTC499
    /// you want to differentiate between those two states.
7ff8adafe9e4atagen2026-10-22 12:30 UTC500
    pub async fn backup_exists_on_server(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC501
        Ok(self.inner.backups().fetch_exists_on_server().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC502
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC503
7ff8adafe9e4atagen2026-10-22 12:30 UTC504
    pub fn recovery_state(&self) -> RecoveryState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC505
        self.inner.recovery().state().into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC506
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC507
7ff8adafe9e4atagen2026-10-22 12:30 UTC508
    pub fn recovery_state_listener(
7ff8adafe9e4atagen2026-10-22 12:30 UTC509
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC510
        listener: Box<dyn RecoveryStateListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC511
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC512
        let mut stream = self.inner.recovery().state_stream();
7ff8adafe9e4atagen2026-10-22 12:30 UTC513
7ff8adafe9e4atagen2026-10-22 12:30 UTC514
        let stream_task = TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC515
            while let Some(state) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC516
                listener.on_update(state.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC517
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC518
        }));
7ff8adafe9e4atagen2026-10-22 12:30 UTC519
7ff8adafe9e4atagen2026-10-22 12:30 UTC520
        stream_task.into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC521
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC522
7ff8adafe9e4atagen2026-10-22 12:30 UTC523
    pub async fn enable_backups(&self) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC524
        Ok(self.inner.recovery().enable_backup().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC525
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC526
7ff8adafe9e4atagen2026-10-22 12:30 UTC527
    pub async fn is_last_device(&self) -> Result<bool> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC528
        Ok(self.inner.recovery().is_last_device().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC529
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC530
7ff8adafe9e4atagen2026-10-22 12:30 UTC531
    /// Does the user have other devices that the current device can verify
7ff8adafe9e4atagen2026-10-22 12:30 UTC532
    /// against?
7ff8adafe9e4atagen2026-10-22 12:30 UTC533
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC534
    /// The device must be signed by the user's cross-signing key, must have an
7ff8adafe9e4atagen2026-10-22 12:30 UTC535
    /// identity, and must not be a dehydrated device.
7ff8adafe9e4atagen2026-10-22 12:30 UTC536
    pub async fn has_devices_to_verify_against(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC537
        Ok(self.inner.has_devices_to_verify_against().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC538
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC539
7ff8adafe9e4atagen2026-10-22 12:30 UTC540
    pub async fn wait_for_backup_upload_steady_state(
7ff8adafe9e4atagen2026-10-22 12:30 UTC541
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC542
        progress_listener: Option<Box<dyn BackupSteadyStateListener>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC543
    ) -> Result<(), SteadyStateError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC544
        let backups = self.inner.backups();
7ff8adafe9e4atagen2026-10-22 12:30 UTC545
        let wait_for_steady_state = backups.wait_for_steady_state();
7ff8adafe9e4atagen2026-10-22 12:30 UTC546
7ff8adafe9e4atagen2026-10-22 12:30 UTC547
        let task = if let Some(listener) = progress_listener {
7ff8adafe9e4atagen2026-10-22 12:30 UTC548
            let mut progress_stream = wait_for_steady_state.subscribe_to_progress();
7ff8adafe9e4atagen2026-10-22 12:30 UTC549
7ff8adafe9e4atagen2026-10-22 12:30 UTC550
            Some(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC551
                while let Some(progress) = progress_stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC552
                    let Ok(progress) = progress else { continue };
7ff8adafe9e4atagen2026-10-22 12:30 UTC553
                    listener.on_update(progress.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC554
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC555
            }))
7ff8adafe9e4atagen2026-10-22 12:30 UTC556
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC557
            None
7ff8adafe9e4atagen2026-10-22 12:30 UTC558
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC559
7ff8adafe9e4atagen2026-10-22 12:30 UTC560
        let result = wait_for_steady_state.await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC561
7ff8adafe9e4atagen2026-10-22 12:30 UTC562
        if let Some(task) = task {
7ff8adafe9e4atagen2026-10-22 12:30 UTC563
            task.abort();
7ff8adafe9e4atagen2026-10-22 12:30 UTC564
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC565
7ff8adafe9e4atagen2026-10-22 12:30 UTC566
        Ok(result?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC567
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC568
7ff8adafe9e4atagen2026-10-22 12:30 UTC569
    pub async fn enable_recovery(
7ff8adafe9e4atagen2026-10-22 12:30 UTC570
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC571
        wait_for_backups_to_upload: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC572
        mut passphrase: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC573
        progress_listener: Box<dyn EnableRecoveryProgressListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC574
    ) -> Result<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC575
        let recovery = self.inner.recovery();
7ff8adafe9e4atagen2026-10-22 12:30 UTC576
7ff8adafe9e4atagen2026-10-22 12:30 UTC577
        let enable = if wait_for_backups_to_upload {
7ff8adafe9e4atagen2026-10-22 12:30 UTC578
            recovery.enable().wait_for_backups_to_upload()
7ff8adafe9e4atagen2026-10-22 12:30 UTC579
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC580
            recovery.enable()
7ff8adafe9e4atagen2026-10-22 12:30 UTC581
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC582
7ff8adafe9e4atagen2026-10-22 12:30 UTC583
        let enable = if let Some(passphrase) = &passphrase {
7ff8adafe9e4atagen2026-10-22 12:30 UTC584
            enable.with_passphrase(passphrase)
7ff8adafe9e4atagen2026-10-22 12:30 UTC585
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC586
            enable
7ff8adafe9e4atagen2026-10-22 12:30 UTC587
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC588
7ff8adafe9e4atagen2026-10-22 12:30 UTC589
        let mut progress_stream = enable.subscribe_to_progress();
7ff8adafe9e4atagen2026-10-22 12:30 UTC590
7ff8adafe9e4atagen2026-10-22 12:30 UTC591
        let task = get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC592
            while let Some(progress) = progress_stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC593
                let Ok(progress) = progress else { continue };
7ff8adafe9e4atagen2026-10-22 12:30 UTC594
                progress_listener.on_update(progress.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC595
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC596
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC597
7ff8adafe9e4atagen2026-10-22 12:30 UTC598
        let ret = enable.await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC599
7ff8adafe9e4atagen2026-10-22 12:30 UTC600
        task.abort();
7ff8adafe9e4atagen2026-10-22 12:30 UTC601
        passphrase.zeroize();
7ff8adafe9e4atagen2026-10-22 12:30 UTC602
7ff8adafe9e4atagen2026-10-22 12:30 UTC603
        Ok(ret)
7ff8adafe9e4atagen2026-10-22 12:30 UTC604
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC605
7ff8adafe9e4atagen2026-10-22 12:30 UTC606
    pub async fn disable_recovery(&self) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC607
        Ok(self.inner.recovery().disable().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC608
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC609
7ff8adafe9e4atagen2026-10-22 12:30 UTC610
    pub async fn reset_recovery_key(&self) -> Result<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC611
        Ok(self.inner.recovery().reset_key().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC612
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC613
7ff8adafe9e4atagen2026-10-22 12:30 UTC614
    pub async fn recover_and_reset(&self, mut old_recovery_key: String) -> Result<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC615
        let result = self.inner.recovery().recover_and_reset(&old_recovery_key).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC616
7ff8adafe9e4atagen2026-10-22 12:30 UTC617
        old_recovery_key.zeroize();
7ff8adafe9e4atagen2026-10-22 12:30 UTC618
7ff8adafe9e4atagen2026-10-22 12:30 UTC619
        Ok(result?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC620
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC621
7ff8adafe9e4atagen2026-10-22 12:30 UTC622
    /// Completely reset the current user's crypto identity: reset the cross
7ff8adafe9e4atagen2026-10-22 12:30 UTC623
    /// signing keys, delete the existing backup and recovery key.
7ff8adafe9e4atagen2026-10-22 12:30 UTC624
    pub async fn reset_identity(&self) -> Result<Option<Arc<IdentityResetHandle>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC625
        if let Some(reset_handle) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC626
            self.inner.recovery().reset_identity().await.map_err(ClientError::from_err)?
7ff8adafe9e4atagen2026-10-22 12:30 UTC627
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC628
            return Ok(Some(Arc::new(IdentityResetHandle { inner: reset_handle })));
7ff8adafe9e4atagen2026-10-22 12:30 UTC629
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC630
7ff8adafe9e4atagen2026-10-22 12:30 UTC631
        Ok(None)
7ff8adafe9e4atagen2026-10-22 12:30 UTC632
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC633
7ff8adafe9e4atagen2026-10-22 12:30 UTC634
    /// Download identity and key backup information from Recovery
7ff8adafe9e4atagen2026-10-22 12:30 UTC635
    pub async fn recover(&self, mut recovery_key: String) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC636
        let result = self.inner.recovery().recover(&recovery_key).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC637
7ff8adafe9e4atagen2026-10-22 12:30 UTC638
        recovery_key.zeroize();
7ff8adafe9e4atagen2026-10-22 12:30 UTC639
7ff8adafe9e4atagen2026-10-22 12:30 UTC640
        Ok(result?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC641
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC642
7ff8adafe9e4atagen2026-10-22 12:30 UTC643
    /// Download identity and key backup information from Recovery, and, if the
7ff8adafe9e4atagen2026-10-22 12:30 UTC644
    /// key backup information is inconsistent, create a new key backup.
7ff8adafe9e4atagen2026-10-22 12:30 UTC645
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC646
    /// This will create a new key backup if:
7ff8adafe9e4atagen2026-10-22 12:30 UTC647
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC648
    /// * Key backup is enabled and the backup decryption key is missing from
7ff8adafe9e4atagen2026-10-22 12:30 UTC649
    ///   Recovery, or
7ff8adafe9e4atagen2026-10-22 12:30 UTC650
    /// * Key backup is enabled and the backup decryption key does not match the
7ff8adafe9e4atagen2026-10-22 12:30 UTC651
    ///   public key
7ff8adafe9e4atagen2026-10-22 12:30 UTC652
    pub async fn recover_and_fix_backup(&self, mut recovery_key: String) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC653
        let result = self.inner.recovery().recover_and_fix_backup(&recovery_key).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC654
7ff8adafe9e4atagen2026-10-22 12:30 UTC655
        recovery_key.zeroize();
7ff8adafe9e4atagen2026-10-22 12:30 UTC656
7ff8adafe9e4atagen2026-10-22 12:30 UTC657
        Ok(result?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC658
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC659
7ff8adafe9e4atagen2026-10-22 12:30 UTC660
    pub fn verification_state(&self) -> VerificationState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC661
        self.inner.verification_state().get().into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC662
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC663
7ff8adafe9e4atagen2026-10-22 12:30 UTC664
    pub fn verification_state_listener(
7ff8adafe9e4atagen2026-10-22 12:30 UTC665
        self: Arc<Self>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC666
        listener: Box<dyn VerificationStateListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC667
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC668
        let mut subscriber = self.inner.verification_state();
7ff8adafe9e4atagen2026-10-22 12:30 UTC669
7ff8adafe9e4atagen2026-10-22 12:30 UTC670
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC671
            while let Some(verification_state) = subscriber.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC672
                listener.on_update(verification_state.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC673
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC674
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC675
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC676
7ff8adafe9e4atagen2026-10-22 12:30 UTC677
    /// Waits for end-to-end encryption initialization tasks to finish, if any
7ff8adafe9e4atagen2026-10-22 12:30 UTC678
    /// was running in the background.
7ff8adafe9e4atagen2026-10-22 12:30 UTC679
    pub async fn wait_for_e2ee_initialization_tasks(&self) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC680
        self.inner.wait_for_e2ee_initialization_tasks().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC681
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC682
7ff8adafe9e4atagen2026-10-22 12:30 UTC683
    /// Get the E2EE identity of a user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC684
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC685
    /// This method always tries to fetch the identity from the store, which we
7ff8adafe9e4atagen2026-10-22 12:30 UTC686
    /// only have if the user is tracked, meaning that we are both members
7ff8adafe9e4atagen2026-10-22 12:30 UTC687
    /// of the same encrypted room. If no user is found locally, a request will
7ff8adafe9e4atagen2026-10-22 12:30 UTC688
    /// be made to the homeserver unless `fallback_to_server` is set to `false`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC689
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC690
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC691
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC692
    /// * `user_id` - The ID of the user that the identity belongs to.
7ff8adafe9e4atagen2026-10-22 12:30 UTC693
    /// * `fallback_to_server` - Should we request the user identity from the
7ff8adafe9e4atagen2026-10-22 12:30 UTC694
    ///   homeserver if one isn't found locally.
7ff8adafe9e4atagen2026-10-22 12:30 UTC695
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC696
    /// Returns a `UserIdentity` if one is found. Returns an error if there
7ff8adafe9e4atagen2026-10-22 12:30 UTC697
    /// was an issue with the crypto store or with the request to the
7ff8adafe9e4atagen2026-10-22 12:30 UTC698
    /// homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC699
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC700
    /// This will always return `None` if the client hasn't been logged in.
7ff8adafe9e4atagen2026-10-22 12:30 UTC701
    pub async fn user_identity(
7ff8adafe9e4atagen2026-10-22 12:30 UTC702
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC703
        user_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC704
        fallback_to_server: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC705
    ) -> Result<Option<Arc<UserIdentity>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC706
        match self.inner.get_user_identity(user_id.as_str().try_into()?).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC707
            Ok(Some(identity)) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC708
                return Ok(Some(Arc::new(UserIdentity { inner: identity })));
7ff8adafe9e4atagen2026-10-22 12:30 UTC709
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC710
            Ok(None) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC711
                info!("No identity found in the store.");
7ff8adafe9e4atagen2026-10-22 12:30 UTC712
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC713
            Err(error) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC714
                error!("Failed fetching identity from the store: {error}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC715
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC716
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC717
7ff8adafe9e4atagen2026-10-22 12:30 UTC718
        info!("Requesting identity from the server.");
7ff8adafe9e4atagen2026-10-22 12:30 UTC719
7ff8adafe9e4atagen2026-10-22 12:30 UTC720
        if fallback_to_server {
7ff8adafe9e4atagen2026-10-22 12:30 UTC721
            let identity = self.inner.request_user_identity(user_id.as_str().try_into()?).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC722
            Ok(identity.map(|identity| Arc::new(UserIdentity { inner: identity })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC723
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC724
            Ok(None)
7ff8adafe9e4atagen2026-10-22 12:30 UTC725
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC726
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC727
7ff8adafe9e4atagen2026-10-22 12:30 UTC728
    /// This method will import all the private cross-signing keys and
7ff8adafe9e4atagen2026-10-22 12:30 UTC729
    /// the private part of a backup key and its accompanying version into the
7ff8adafe9e4atagen2026-10-22 12:30 UTC730
    /// store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC731
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC732
    /// Importing all the secrets will mark the device as verified and enable
7ff8adafe9e4atagen2026-10-22 12:30 UTC733
    /// backups.
7ff8adafe9e4atagen2026-10-22 12:30 UTC734
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC735
    /// **Warning**: Only import this from a trusted source, i.e. if an existing
7ff8adafe9e4atagen2026-10-22 12:30 UTC736
    /// device is sharing this with a new device.
7ff8adafe9e4atagen2026-10-22 12:30 UTC737
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC738
    /// **Warning*: Only call this method right after logging in and before the
7ff8adafe9e4atagen2026-10-22 12:30 UTC739
    /// initial sync has been started.
7ff8adafe9e4atagen2026-10-22 12:30 UTC740
    pub async fn import_secrets_bundle(
7ff8adafe9e4atagen2026-10-22 12:30 UTC741
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC742
        secrets_bundle: &SecretsBundleWithUserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC743
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC744
        let user_id = self._client.inner.user_id().expect(
7ff8adafe9e4atagen2026-10-22 12:30 UTC745
            "We should have a user ID available now, this is only called once we're logged in",
7ff8adafe9e4atagen2026-10-22 12:30 UTC746
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC747
7ff8adafe9e4atagen2026-10-22 12:30 UTC748
        if user_id == secrets_bundle.user_id {
7ff8adafe9e4atagen2026-10-22 12:30 UTC749
            self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC750
                .import_secrets_bundle(&secrets_bundle.inner)
7ff8adafe9e4atagen2026-10-22 12:30 UTC751
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC752
                .map_err(ClientError::from_err)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC753
7ff8adafe9e4atagen2026-10-22 12:30 UTC754
            self.inner.wait_for_e2ee_initialization_tasks().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC755
7ff8adafe9e4atagen2026-10-22 12:30 UTC756
            Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC757
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC758
            Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC759
                msg: "Secrets bundle does not belong to the user which was logged in".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC760
                details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC761
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC762
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC763
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC764
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC765
7ff8adafe9e4atagen2026-10-22 12:30 UTC766
/// The E2EE identity of a user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC767
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC768
pub struct UserIdentity {
7ff8adafe9e4atagen2026-10-22 12:30 UTC769
    inner: matrix_sdk::encryption::identities::UserIdentity,
7ff8adafe9e4atagen2026-10-22 12:30 UTC770
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC771
7ff8adafe9e4atagen2026-10-22 12:30 UTC772
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC773
impl UserIdentity {
7ff8adafe9e4atagen2026-10-22 12:30 UTC774
    /// Remember this identity, ensuring it does not result in a pin violation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC775
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC776
    /// When we first see a user, we assume their cryptographic identity has not
7ff8adafe9e4atagen2026-10-22 12:30 UTC777
    /// been tampered with by the homeserver or another entity with
7ff8adafe9e4atagen2026-10-22 12:30 UTC778
    /// man-in-the-middle capabilities. We remember this identity and call this
7ff8adafe9e4atagen2026-10-22 12:30 UTC779
    /// action "pinning".
7ff8adafe9e4atagen2026-10-22 12:30 UTC780
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC781
    /// If the identity presented for the user changes later on, the newly
7ff8adafe9e4atagen2026-10-22 12:30 UTC782
    /// presented identity is considered to be in "pin violation". This
7ff8adafe9e4atagen2026-10-22 12:30 UTC783
    /// method explicitly accepts the new identity, allowing it to replace
7ff8adafe9e4atagen2026-10-22 12:30 UTC784
    /// the previously pinned one and bringing it out of pin violation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC785
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC786
    /// UIs should display a warning to the user when encountering an identity
7ff8adafe9e4atagen2026-10-22 12:30 UTC787
    /// which is not verified and is in pin violation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC788
    pub(crate) async fn pin(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC789
        Ok(self.inner.pin().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC790
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC791
7ff8adafe9e4atagen2026-10-22 12:30 UTC792
    /// Get the public part of the Master key of this user identity.
7ff8adafe9e4atagen2026-10-22 12:30 UTC793
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC794
    /// The public part of the Master key is usually used to uniquely identify
7ff8adafe9e4atagen2026-10-22 12:30 UTC795
    /// the identity.
7ff8adafe9e4atagen2026-10-22 12:30 UTC796
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC797
    /// Returns None if the master key does not actually contain any keys.
7ff8adafe9e4atagen2026-10-22 12:30 UTC798
    pub(crate) fn master_key(&self) -> Option<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC799
        self.inner.master_key().get_first_key().map(|k| k.to_base64())
7ff8adafe9e4atagen2026-10-22 12:30 UTC800
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC801
7ff8adafe9e4atagen2026-10-22 12:30 UTC802
    /// Is the user identity considered to be verified.
7ff8adafe9e4atagen2026-10-22 12:30 UTC803
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC804
    /// If the identity belongs to another user, our own user identity needs to
7ff8adafe9e4atagen2026-10-22 12:30 UTC805
    /// be verified as well for the identity to be considered to be verified.
7ff8adafe9e4atagen2026-10-22 12:30 UTC806
    pub fn is_verified(&self) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC807
        self.inner.is_verified()
7ff8adafe9e4atagen2026-10-22 12:30 UTC808
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC809
7ff8adafe9e4atagen2026-10-22 12:30 UTC810
    /// True if we verified this identity at some point in the past.
7ff8adafe9e4atagen2026-10-22 12:30 UTC811
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC812
    /// To reset this latch back to `false`, one must call
7ff8adafe9e4atagen2026-10-22 12:30 UTC813
    /// [`UserIdentity::withdraw_verification()`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC814
    pub fn was_previously_verified(&self) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC815
        self.inner.was_previously_verified()
7ff8adafe9e4atagen2026-10-22 12:30 UTC816
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC817
7ff8adafe9e4atagen2026-10-22 12:30 UTC818
    /// Remove the requirement for this identity to be verified.
7ff8adafe9e4atagen2026-10-22 12:30 UTC819
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC820
    /// If an identity was previously verified and is not anymore it will be
7ff8adafe9e4atagen2026-10-22 12:30 UTC821
    /// reported to the user. In order to remove this notice users have to
7ff8adafe9e4atagen2026-10-22 12:30 UTC822
    /// verify again or to withdraw the verification requirement.
7ff8adafe9e4atagen2026-10-22 12:30 UTC823
    pub(crate) async fn withdraw_verification(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC824
        Ok(self.inner.withdraw_verification().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC825
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC826
7ff8adafe9e4atagen2026-10-22 12:30 UTC827
    /// Was this identity previously verified, and is no longer?
7ff8adafe9e4atagen2026-10-22 12:30 UTC828
    pub fn has_verification_violation(&self) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC829
        self.inner.has_verification_violation()
7ff8adafe9e4atagen2026-10-22 12:30 UTC830
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC831
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC832
7ff8adafe9e4atagen2026-10-22 12:30 UTC833
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC834
pub struct IdentityResetHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC835
    pub(crate) inner: matrix_sdk::encryption::recovery::IdentityResetHandle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC836
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC837
7ff8adafe9e4atagen2026-10-22 12:30 UTC838
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC839
impl IdentityResetHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC840
    /// Get the underlying [`CrossSigningResetAuthType`] this identity reset
7ff8adafe9e4atagen2026-10-22 12:30 UTC841
    /// process is using.
7ff8adafe9e4atagen2026-10-22 12:30 UTC842
    pub fn auth_type(&self) -> CrossSigningResetAuthType {
7ff8adafe9e4atagen2026-10-22 12:30 UTC843
        self.inner.auth_type().into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC844
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC845
7ff8adafe9e4atagen2026-10-22 12:30 UTC846
    /// This method starts the identity reset process and
7ff8adafe9e4atagen2026-10-22 12:30 UTC847
    /// will go through the following steps:
7ff8adafe9e4atagen2026-10-22 12:30 UTC848
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC849
    /// 1. Disable backing up room keys and delete the active backup
7ff8adafe9e4atagen2026-10-22 12:30 UTC850
    /// 2. Disable recovery and delete secret storage
7ff8adafe9e4atagen2026-10-22 12:30 UTC851
    /// 3. Go through the cross-signing key reset flow
7ff8adafe9e4atagen2026-10-22 12:30 UTC852
    /// 4. Finally, re-enable key backups only if they were enabled before
7ff8adafe9e4atagen2026-10-22 12:30 UTC853
    pub async fn reset(&self, auth: Option<AuthData>) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC854
        self.inner.reset(auth.map(Into::into)).await.map_err(ClientError::from_err)
7ff8adafe9e4atagen2026-10-22 12:30 UTC855
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC856
7ff8adafe9e4atagen2026-10-22 12:30 UTC857
    pub async fn cancel(&self) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC858
        self.inner.cancel().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC859
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC860
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC861
7ff8adafe9e4atagen2026-10-22 12:30 UTC862
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC863
pub enum CrossSigningResetAuthType {
7ff8adafe9e4atagen2026-10-22 12:30 UTC864
    /// The homeserver requires user-interactive authentication.
7ff8adafe9e4atagen2026-10-22 12:30 UTC865
    Uiaa,
7ff8adafe9e4atagen2026-10-22 12:30 UTC866
    /// OAuth is used for authentication and the user needs to open a URL to
7ff8adafe9e4atagen2026-10-22 12:30 UTC867
    /// approve the upload of cross-signing keys.
7ff8adafe9e4atagen2026-10-22 12:30 UTC868
    OAuth { info: OAuthCrossSigningResetInfo },
7ff8adafe9e4atagen2026-10-22 12:30 UTC869
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC870
7ff8adafe9e4atagen2026-10-22 12:30 UTC871
impl From<&matrix_sdk::encryption::CrossSigningResetAuthType> for CrossSigningResetAuthType {
7ff8adafe9e4atagen2026-10-22 12:30 UTC872
    fn from(value: &matrix_sdk::encryption::CrossSigningResetAuthType) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC873
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC874
            encryption::CrossSigningResetAuthType::Uiaa(_) => Self::Uiaa,
7ff8adafe9e4atagen2026-10-22 12:30 UTC875
            encryption::CrossSigningResetAuthType::OAuth(info) => Self::OAuth { info: info.into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC876
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC877
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC878
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC879
7ff8adafe9e4atagen2026-10-22 12:30 UTC880
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC881
pub struct OAuthCrossSigningResetInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC882
    /// The URL where the user can approve the reset of the cross-signing keys.
7ff8adafe9e4atagen2026-10-22 12:30 UTC883
    pub approval_url: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC884
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC885
7ff8adafe9e4atagen2026-10-22 12:30 UTC886
impl From<&matrix_sdk::encryption::OAuthCrossSigningResetInfo> for OAuthCrossSigningResetInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC887
    fn from(value: &matrix_sdk::encryption::OAuthCrossSigningResetInfo) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC888
        Self { approval_url: value.approval_url.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC889
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC890
}