diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp index 029edb50e..205b5f7f6 100644 --- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp @@ -1,64 +1,79 @@ #include "BackupOperationsExecutor.h" #include "DatabaseManager.h" #include "GlobalDBSingleton.h" #include "Logger.h" #include "RustPromiseManager.h" #include "WorkerThread.h" #include "lib.rs.h" namespace comm { void BackupOperationsExecutor::createMainCompaction( std::string backupID, size_t futureID) { taskType job = [backupID, futureID]() { try { DatabaseManager::getQueryExecutor().createMainCompaction(backupID); ::resolveUnitFuture(futureID); } catch (const std::exception &e) { ::rejectFuture(futureID, rust::String(e.what())); Logger::log( "Main compaction creation failed. Details: " + std::string(e.what())); } }; GlobalDBSingleton::instance.scheduleOrRunCancellable(job); } void BackupOperationsExecutor::restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey, std::string maxVersion, size_t futureID) { taskType job = [mainCompactionPath, mainCompactionEncryptionKey, maxVersion, futureID]() { try { DatabaseManager::getQueryExecutor().restoreFromMainCompaction( mainCompactionPath, mainCompactionEncryptionKey, maxVersion); ::resolveUnitFuture(futureID); } catch (const std::exception &e) { std::string errorDetails = std::string(e.what()); Logger::log( "Restore from main compaction failed. Details: " + errorDetails); ::rejectFuture(futureID, errorDetails); } }; GlobalDBSingleton::instance.scheduleOrRunCancellable(job); } void BackupOperationsExecutor::restoreFromBackupLog( const std::vector &backupLog, size_t futureID) { taskType job = [backupLog, futureID]() { try { DatabaseManager::getQueryExecutor().restoreFromBackupLog(backupLog); ::resolveUnitFuture(futureID); } catch (const std::exception &e) { std::string errorDetails = std::string(e.what()); Logger::log("Restore from backup log failed. Details: " + errorDetails); ::rejectFuture(futureID, errorDetails); } }; GlobalDBSingleton::instance.scheduleOrRunCancellable(job); } + +void BackupOperationsExecutor::setBackupID( + std::string backupID, + size_t futureID) { + taskType job = [backupID, futureID]() { + try { + DatabaseManager::getQueryExecutor().setMetadata("backupID", backupID); + ::resolveUnitFuture(futureID); + } catch (const std::exception &e) { + ::rejectFuture(futureID, rust::String(e.what())); + Logger::log("Setting backupID failed. Details: " + std::string(e.what())); + } + }; + GlobalDBSingleton::instance.scheduleOrRunCancellable(job); +} } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h index 76c10d478..13202fc7c 100644 --- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h @@ -1,19 +1,20 @@ #pragma once #include #include namespace comm { class BackupOperationsExecutor { public: static void createMainCompaction(std::string backupID, size_t futureID); static void restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey, std::string maxVersion, size_t futureID); static void restoreFromBackupLog( const std::vector &backupLog, size_t futureID); + static void setBackupID(std::string backupID, size_t futureID); }; } // namespace comm diff --git a/native/native_rust_library/RustBackupExecutor.cpp b/native/native_rust_library/RustBackupExecutor.cpp index 40669abfc..81dc3c22c 100644 --- a/native/native_rust_library/RustBackupExecutor.cpp +++ b/native/native_rust_library/RustBackupExecutor.cpp @@ -1,56 +1,60 @@ #include "RustBackupExecutor.h" #include "../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h" #include "../cpp/CommonCpp/Tools/PlatformSpecificTools.h" #include namespace comm { rust::String getBackupDirectoryPath() { return rust::String(PlatformSpecificTools::getBackupDirectoryPath()); } rust::String getBackupFilePath(rust::Str backupID, bool isAttachments) { return rust::String(PlatformSpecificTools::getBackupFilePath( std::string(backupID), isAttachments)); } rust::String getBackupLogFilePath(rust::Str backupID, rust::Str logID, bool isAttachments) { return rust::String(PlatformSpecificTools::getBackupLogFilePath( std::string(backupID), std::string(logID), isAttachments)); } rust::String getBackupUserKeysFilePath(rust::Str backupID) { return rust::String( PlatformSpecificTools::getBackupUserKeysFilePath(std::string(backupID))); } rust::String getSIWEBackupMessagePath(rust::Str backupID) { return rust::String( PlatformSpecificTools::getSIWEBackupMessagePath(std::string(backupID))); } void createMainCompaction(rust::Str backupID, size_t futureID) { BackupOperationsExecutor::createMainCompaction( std::string(backupID), futureID); } +void setBackupID(rust::Str backupID, size_t futureID) { + BackupOperationsExecutor::setBackupID(std::string(backupID), futureID); +} + void restoreFromMainCompaction( rust::Str mainCompactionPath, rust::Str mainCompactionEncryptionKey, rust::Str maxVersion, size_t futureID) { BackupOperationsExecutor::restoreFromMainCompaction( std::string(mainCompactionPath), std::string(mainCompactionEncryptionKey), std::string(maxVersion), futureID); } void restoreFromBackupLog(rust::Vec backupLog, size_t futureID) { BackupOperationsExecutor::restoreFromBackupLog( std::move(std::vector(backupLog.begin(), backupLog.end())), futureID); } } // namespace comm diff --git a/native/native_rust_library/RustBackupExecutor.h b/native/native_rust_library/RustBackupExecutor.h index e4877cc65..94414a49e 100644 --- a/native/native_rust_library/RustBackupExecutor.h +++ b/native/native_rust_library/RustBackupExecutor.h @@ -1,21 +1,22 @@ #pragma once #include "cxx.h" namespace comm { rust::String getBackupDirectoryPath(); rust::String getBackupFilePath(rust::Str backupID, bool isAttachments); rust::String getBackupLogFilePath(rust::Str backupID, rust::Str logID, bool isAttachments); rust::String getBackupUserKeysFilePath(rust::Str backupID); rust::String getSIWEBackupMessagePath(rust::Str backupID); void createMainCompaction(rust::Str backupID, size_t futureID); void restoreFromMainCompaction( rust::Str mainCompactionPath, rust::Str mainCompactionEncryptionKey, rust::Str maxVersion, size_t futureID); void restoreFromBackupLog(rust::Vec backupLog, size_t futureID); +void setBackupID(rust::Str backupID, size_t futureID); } // namespace comm diff --git a/native/native_rust_library/src/backup.rs b/native/native_rust_library/src/backup.rs index a741d4777..b84176d03 100644 --- a/native/native_rust_library/src/backup.rs +++ b/native/native_rust_library/src/backup.rs @@ -1,572 +1,579 @@ mod compaction_upload_promises; mod file_info; mod upload_handler; use crate::argon2_tools::{compute_backup_key, compute_backup_key_str}; use crate::constants::{aes, secure_store}; use crate::ffi::{ create_main_compaction, get_backup_directory_path, get_backup_user_keys_file_path, get_siwe_backup_message_path, restore_from_backup_log, restore_from_main_compaction, secure_store_get, - string_callback, void_callback, + set_backup_id, string_callback, void_callback, }; use crate::utils::future_manager; use crate::utils::jsi_callbacks::handle_string_result_as_callback; use crate::BACKUP_SOCKET_ADDR; use crate::RUNTIME; use backup_client::{ BackupClient, BackupDescriptor, LatestBackupInfoResponse, RequestedData, TryStreamExt, UserIdentity, }; use serde::{Deserialize, Serialize}; use siwe::Message; use std::error::Error; use std::path::PathBuf; pub mod ffi { use super::*; pub use upload_handler::ffi::*; fn handle_backup_creation_error(backup_id: String, err: String) { compaction_upload_promises::resolve(&backup_id, Err(err)); tokio::spawn(upload_handler::compaction::cleanup_files(backup_id)); } async fn prepare_user_keys_backup( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, siwe_backup_msg: String, ) -> Result<(), String> { let result = create_user_keys_compaction( backup_id.clone(), backup_secret, pickle_key, pickled_account, ) .await .map_err(|err| err.to_string()); if let Err(err) = result { handle_backup_creation_error(backup_id.clone(), err.to_string()); return Err(err); } if !siwe_backup_msg.is_empty() { if let Err(err) = create_siwe_backup_msg_compaction(&backup_id, siwe_backup_msg).await { handle_backup_creation_error(backup_id.clone(), err.to_string()); return Err(err.to_string()); } } Ok(()) } pub fn create_backup( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, siwe_backup_msg: String, promise_id: u32, ) { compaction_upload_promises::insert(backup_id.clone(), promise_id); RUNTIME.spawn(async move { if (prepare_user_keys_backup( backup_id.clone(), backup_secret, pickle_key, pickled_account, siwe_backup_msg.clone(), ) .await) .is_err() { return; } let (future_id, future) = future_manager::new_future::<()>().await; create_main_compaction(&backup_id, future_id); if let Err(err) = future.await { handle_backup_creation_error(backup_id.clone(), err.to_string()); return; } trigger_backup_file_upload(); // The promise will be resolved when the backup is uploaded }); } pub fn create_user_keys_backup( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, siwe_backup_msg: String, promise_id: u32, ) { compaction_upload_promises::insert(backup_id.clone(), promise_id); RUNTIME.spawn(async move { if (prepare_user_keys_backup( backup_id.clone(), backup_secret, pickle_key, pickled_account, siwe_backup_msg.clone(), ) .await) .is_err() { return; } + let (future_id, future) = future_manager::new_future::<()>().await; + set_backup_id(&backup_id, future_id); + if let Err(err) = future.await { + handle_backup_creation_error(backup_id.clone(), err.to_string()); + return; + } + trigger_backup_file_upload(); // The promise will be resolved when the backup is uploaded }); } pub fn restore_backup( backup_secret: String, backup_id: String, max_version: String, promise_id: u32, ) { RUNTIME.spawn(async move { let result = download_backup(backup_secret, backup_id) .await .map_err(|err| err.to_string()); let result = match result { Ok(result) => result, Err(error) => { void_callback(error, promise_id); return; } }; let (future_id, future) = future_manager::new_future::<()>().await; restore_from_main_compaction( &result.backup_restoration_path.to_string_lossy(), &result.backup_data_key, &max_version, future_id, ); if let Err(error) = future.await { void_callback(error, promise_id); return; } if let Err(error) = download_and_apply_logs(&result.backup_id, result.backup_log_data_key) .await { void_callback(error.to_string(), promise_id); return; } void_callback(String::new(), promise_id); }); } pub fn retrieve_backup_keys( backup_secret: String, backup_id: String, promise_id: u32, ) { RUNTIME.spawn(async move { let result = download_backup_keys(backup_id, backup_secret) .await .map_err(|err| err.to_string()); let result = match result { Ok(result) => result, Err(error) => { string_callback(error, promise_id, "".to_string()); return; } }; let serialize_result = serde_json::to_string(&result); handle_string_result_as_callback(serialize_result, promise_id); }); } pub fn restore_backup_data( backup_id: String, backup_data_key: String, backup_log_data_key: String, max_version: String, promise_id: u32, ) { RUNTIME.spawn(async move { let backup_keys = BackupKeysResult { backup_id, backup_data_key, backup_log_data_key, }; let result = download_backup_data(backup_keys) .await .map_err(|err| err.to_string()); let result = match result { Ok(result) => result, Err(error) => { void_callback(error, promise_id); return; } }; let (future_id, future) = future_manager::new_future::<()>().await; restore_from_main_compaction( &result.backup_restoration_path.to_string_lossy(), &result.backup_data_key, &max_version, future_id, ); if let Err(error) = future.await { void_callback(error, promise_id); return; } if let Err(error) = download_and_apply_logs(&result.backup_id, result.backup_log_data_key) .await { void_callback(error.to_string(), promise_id); return; } void_callback(String::new(), promise_id); }); } fn get_siwe_backup_data_from_msg( siwe_backup_msg: String, ) -> Result { let siwe_backup_msg_obj: Message = match siwe_backup_msg.parse() { Ok(siwe_backup_msg_obj) => siwe_backup_msg_obj, Err(error) => { return Err(error.to_string()); } }; let siwe_backup_msg_nonce = siwe_backup_msg_obj.nonce; let siwe_backup_msg_statement = match siwe_backup_msg_obj.statement { Some(statement) => statement, None => { return Err("Backup message invalid: missing statement".to_string()); } }; let siwe_backup_msg_issued_at = siwe_backup_msg_obj.issued_at.to_string(); Ok(SIWEBackupData { siwe_backup_msg_nonce, siwe_backup_msg_statement, siwe_backup_msg_issued_at, }) } pub fn retrieve_latest_backup_info(user_identifier: String, promise_id: u32) { RUNTIME.spawn(async move { let latest_backup_id_response = download_latest_backup_info(user_identifier) .await .map_err(|err| err.to_string()); let result = match latest_backup_id_response { Ok(result) => result, Err(error) => { string_callback(error, promise_id, "".to_string()); return; } }; let LatestBackupInfoResponse { backup_id, user_id, siwe_backup_msg, } = result; let siwe_backup_data = match siwe_backup_msg { Some(siwe_backup_msg_value) => { match get_siwe_backup_data_from_msg(siwe_backup_msg_value) { Ok(data) => Some(data), Err(err) => { string_callback(err, promise_id, "".to_string()); return; } } } None => None, }; let result = LatestBackupInfo { backup_id, user_id, siwe_backup_data, }; let serialize_result = serde_json::to_string(&result); handle_string_result_as_callback(serialize_result, promise_id); }); } } pub async fn create_user_keys_compaction( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, ) -> Result<(), Box> { let mut backup_key = compute_backup_key(backup_secret.as_bytes(), backup_id.as_bytes())?; let backup_data_key = secure_store_get(secure_store::SECURE_STORE_ENCRYPTION_KEY_ID)?; let backup_log_data_key = secure_store_get(secure_store::SECURE_STORE_BACKUP_LOGS_ENCRYPTION_KEY_ID)?; let user_keys = UserKeys { backup_data_key, backup_log_data_key, pickle_key, pickled_account, }; let encrypted_user_keys = user_keys.encrypt(&mut backup_key)?; let user_keys_file = get_backup_user_keys_file_path(&backup_id)?; tokio::fs::write(user_keys_file, encrypted_user_keys).await?; Ok(()) } pub async fn create_siwe_backup_msg_compaction( backup_id: &str, siwe_backup_msg: String, ) -> Result<(), Box> { let siwe_backup_msg_file = get_siwe_backup_message_path(backup_id)?; tokio::fs::write(siwe_backup_msg_file, siwe_backup_msg).await?; Ok(()) } async fn download_backup( backup_secret: String, backup_id: String, ) -> Result> { let backup_keys = download_backup_keys(backup_id, backup_secret).await?; download_backup_data(backup_keys).await } async fn download_latest_backup_info( user_identifier: String, ) -> Result> { let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?; let latest_backup_descriptor = BackupDescriptor::Latest { user_identifier }; let backup_info_response = backup_client .download_backup_data(&latest_backup_descriptor, RequestedData::BackupInfo) .await?; let LatestBackupInfoResponse { backup_id, user_id, siwe_backup_msg, } = serde_json::from_slice(&backup_info_response)?; Ok(LatestBackupInfoResponse { backup_id, user_id, siwe_backup_msg, }) } async fn download_backup_keys( backup_id: String, backup_secret: String, ) -> Result> { let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?; let user_identity = get_user_identity_from_secure_store()?; let backup_descriptor = BackupDescriptor::BackupID { backup_id: backup_id.clone(), user_identity: user_identity.clone(), }; let mut encrypted_user_keys = backup_client .download_backup_data(&backup_descriptor, RequestedData::UserKeys) .await?; let mut backup_key = compute_backup_key_str(&backup_secret, &backup_id)?; let user_keys = UserKeys::from_encrypted(&mut encrypted_user_keys, &mut backup_key)?; Ok(BackupKeysResult { backup_id, backup_data_key: user_keys.backup_data_key, backup_log_data_key: user_keys.backup_log_data_key, }) } async fn download_backup_data( backup_keys: BackupKeysResult, ) -> Result> { let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?; let user_identity = get_user_identity_from_secure_store()?; let BackupKeysResult { backup_id, backup_data_key, backup_log_data_key, } = backup_keys; let backup_data_descriptor = BackupDescriptor::BackupID { backup_id: backup_id.clone(), user_identity: user_identity.clone(), }; let encrypted_user_data = backup_client .download_backup_data(&backup_data_descriptor, RequestedData::UserData) .await?; let backup_restoration_path = PathBuf::from(get_backup_directory_path()?).join("restore_compaction"); tokio::fs::write(&backup_restoration_path, encrypted_user_data).await?; Ok(CompactionDownloadResult { backup_restoration_path, backup_id, backup_data_key, backup_log_data_key, }) } async fn download_and_apply_logs( backup_id: &str, backup_log_data_key: String, ) -> Result<(), Box> { let mut backup_log_data_key = backup_log_data_key.into_bytes(); let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?; let user_identity = get_user_identity_from_secure_store()?; let stream = backup_client.download_logs(&user_identity, backup_id).await; let mut stream = Box::pin(stream); while let Some(mut log) = stream.try_next().await? { let data = decrypt( backup_log_data_key.as_mut_slice(), log.content.as_mut_slice(), )?; let (future_id, future) = future_manager::new_future::<()>().await; restore_from_backup_log(data, future_id); future.await?; } Ok(()) } fn get_user_identity_from_secure_store() -> Result { Ok(UserIdentity { user_id: secure_store_get(secure_store::USER_ID)?, access_token: secure_store_get(secure_store::COMM_SERVICES_ACCESS_TOKEN)?, device_id: secure_store_get(secure_store::DEVICE_ID)?, }) } // This struct should match `BackupKeys` in `lib/types/backup-types.js` #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct BackupKeysResult { #[serde(rename = "backupID")] backup_id: String, backup_data_key: String, backup_log_data_key: String, } // This struct should match `SIWEBackupData` in `lib/types/backup-types.js` #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct SIWEBackupData { siwe_backup_msg_statement: String, siwe_backup_msg_nonce: String, siwe_backup_msg_issued_at: String, } // This struct should match `LatestBackupInfo` in `lib/types/backup-types.js` #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct LatestBackupInfo { #[serde(rename = "backupID")] pub backup_id: String, #[serde(rename = "userID")] pub user_id: String, pub siwe_backup_data: Option, } struct CompactionDownloadResult { backup_id: String, backup_restoration_path: PathBuf, backup_data_key: String, backup_log_data_key: String, } /// Stores the Olm account in `pickled_account`. However, Olm account /// information might be out of date. We have decided we don't need /// to update this when one-time keys (OTKs) or prekeys change. /// The reasoning behind this decision is that the backed-up Olm account /// is primarily used for signing an update to the device list. For these /// operations only the identity signing key is necessary. #[derive(Debug, Serialize, Deserialize)] struct UserKeys { backup_data_key: String, backup_log_data_key: String, pickle_key: String, pickled_account: String, } impl UserKeys { fn encrypt(&self, backup_key: &mut [u8]) -> Result, Box> { let mut json = serde_json::to_vec(self)?; encrypt(backup_key, &mut json) } fn from_encrypted( data: &mut [u8], backup_key: &mut [u8], ) -> Result> { let decrypted = decrypt(backup_key, data)?; Ok(serde_json::from_slice(&decrypted)?) } } fn encrypt(key: &mut [u8], data: &mut [u8]) -> Result, Box> { let encrypted_len = data.len() + aes::IV_LENGTH + aes::TAG_LENGTH; let mut encrypted = vec![0; encrypted_len]; crate::ffi::encrypt(key, data, &mut encrypted)?; Ok(encrypted) } fn decrypt(key: &mut [u8], data: &mut [u8]) -> Result, Box> { let decrypted_len = data.len() - aes::IV_LENGTH - aes::TAG_LENGTH; let mut decrypted = vec![0; decrypted_len]; crate::ffi::decrypt(key, data, &mut decrypted)?; Ok(decrypted) } diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs index 76dd3adb6..0144aa816 100644 --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -1,571 +1,574 @@ use comm_opaque2::grpc::opaque_error_to_grpc_status as handle_error; use grpc_clients::identity::protos::unauth::DeviceType; use lazy_static::lazy_static; use std::sync::Arc; use tokio::runtime::{Builder, Runtime}; use tonic::Status; mod argon2_tools; mod backup; mod constants; mod identity; mod utils; use crate::argon2_tools::compute_backup_key_str; use crate::utils::jsi_callbacks::{ handle_string_result_as_callback, handle_void_result_as_callback, }; mod generated { // We get the CODE_VERSION from this generated file include!(concat!(env!("OUT_DIR"), "/version.rs")); // We get the IDENTITY_SOCKET_ADDR from this generated file include!(concat!(env!("OUT_DIR"), "/socket_config.rs")); } pub use generated::CODE_VERSION; pub use generated::{BACKUP_SOCKET_ADDR, IDENTITY_SOCKET_ADDR}; #[cfg(not(target_os = "android"))] pub const DEVICE_TYPE: DeviceType = DeviceType::Ios; #[cfg(target_os = "android")] pub const DEVICE_TYPE: DeviceType = DeviceType::Android; lazy_static! { static ref RUNTIME: Arc = Arc::new(Builder::new_multi_thread().enable_all().build().unwrap()); } // ffi uses use backup::ffi::*; use identity::ffi::*; use utils::future_manager::ffi::*; #[allow(clippy::too_many_arguments)] #[cxx::bridge] mod ffi { // Identity Service APIs extern "Rust" { #[cxx_name = "identityRegisterPasswordUser"] fn register_password_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, farcaster_id: String, initial_device_list: String, promise_id: u32, ); #[cxx_name = "identityRegisterReservedPasswordUser"] fn register_reserved_password_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, keyserver_message: String, keyserver_signature: String, initial_device_list: String, promise_id: u32, ); #[cxx_name = "identityLogInPasswordUser"] fn log_in_password_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ); #[cxx_name = "identityRegisterWalletUser"] fn register_wallet_user( siwe_message: String, siwe_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, farcaster_id: String, initial_device_list: String, promise_id: u32, ); #[cxx_name = "identityLogInWalletUser"] fn log_in_wallet_user( siwe_message: String, siwe_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ); #[cxx_name = "identityRestoreUser"] fn restore_user( user_id: String, siwe_message: String, siwe_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, device_list: String, promise_id: u32, ); #[cxx_name = "identityUpdateUserPassword"] fn update_user_password( user_id: String, device_id: String, access_token: String, old_password: String, new_password: String, promise_id: u32, ); #[cxx_name = "identityDeleteWalletUser"] fn delete_wallet_user( user_id: String, device_id: String, access_token: String, promise_id: u32, ); #[cxx_name = "identityDeletePasswordUser"] fn delete_password_user( user_id: String, device_id: String, access_token: String, password: String, promise_id: u32, ); #[cxx_name = "identityLogOut"] fn log_out( user_id: String, device_id: String, access_token: String, promise_id: u32, ); #[cxx_name = "identityLogOutPrimaryDevice"] fn log_out_primary_device( user_id: String, device_id: String, access_token: String, signed_device_list: String, promise_id: u32, ); #[cxx_name = "identityLogOutSecondaryDevice"] fn log_out_secondary_device( user_id: String, device_id: String, access_token: String, promise_id: u32, ); #[cxx_name = "identityGetOutboundKeysForUser"] fn get_outbound_keys_for_user( auth_user_id: String, auth_device_id: String, auth_access_token: String, user_id: String, promise_id: u32, ); #[cxx_name = "identityGetInboundKeysForUser"] fn get_inbound_keys_for_user( auth_user_id: String, auth_device_id: String, auth_access_token: String, user_id: String, promise_id: u32, ); #[cxx_name = "identityRefreshUserPrekeys"] fn refresh_user_prekeys( auth_user_id: String, auth_device_id: String, auth_access_token: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, promise_id: u32, ); #[cxx_name = "identityGenerateNonce"] fn generate_nonce(promise_id: u32); #[cxx_name = "identityVersionSupported"] fn version_supported(promise_id: u32); #[cxx_name = "identityUploadOneTimeKeys"] fn upload_one_time_keys( auth_user_id: String, auth_device_id: String, auth_access_token: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ); #[cxx_name = "identityGetKeyserverKeys"] fn get_keyserver_keys( user_id: String, device_id: String, access_token: String, keyserver_id: String, promise_id: u32, ); #[cxx_name = "identityGetDeviceListForUser"] fn get_device_list_for_user( auth_user_id: String, auth_device_id: String, auth_access_token: String, user_id: String, since_timestamp: i64, promise_id: u32, ); #[cxx_name = "identityGetDeviceListsForUsers"] fn get_device_lists_for_users( auth_user_id: String, auth_device_id: String, auth_access_token: String, user_ids: Vec, promise_id: u32, ); #[cxx_name = "identityUpdateDeviceList"] fn update_device_list( auth_user_id: String, auth_device_id: String, auth_access_token: String, update_payload: String, promise_id: u32, ); #[cxx_name = "identitySyncPlatformDetails"] fn sync_platform_details( auth_user_id: String, auth_device_id: String, auth_access_token: String, promise_id: u32, ); #[cxx_name = "identityUploadSecondaryDeviceKeysAndLogIn"] fn upload_secondary_device_keys_and_log_in( user_id: String, nonce: String, nonce_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ); #[cxx_name = "identityLogInExistingDevice"] fn log_in_existing_device( user_id: String, device_id: String, nonce: String, nonce_signature: String, promise_id: u32, ); #[cxx_name = "identityFindUserIDForWalletAddress"] fn find_user_id_for_wallet_address(wallet_address: String, promise_id: u32); #[cxx_name = "identityFindUserIDForUsername"] fn find_user_id_for_username(username: String, promise_id: u32); // Farcaster #[cxx_name = "identityGetFarcasterUsers"] fn get_farcaster_users(farcaster_ids: Vec, promise_id: u32); #[cxx_name = "identityLinkFarcasterAccount"] fn link_farcaster_account( user_id: String, device_id: String, access_token: String, farcaster_id: String, promise_id: u32, ); #[cxx_name = "identityUnlinkFarcasterAccount"] fn unlink_farcaster_account( user_id: String, device_id: String, access_token: String, promise_id: u32, ); #[cxx_name = "identityFindUserIdentities"] fn find_user_identities( user_id: String, device_id: String, access_token: String, user_ids: Vec, promise_id: u32, ); // Argon2 #[cxx_name = "compute_backup_key"] fn compute_backup_key_str( password: &str, backup_id: &str, ) -> Result<[u8; 32]>; } unsafe extern "C++" { include!("RustCallback.h"); #[namespace = "comm"] #[cxx_name = "stringCallback"] fn string_callback(error: String, promise_id: u32, ret: String); #[namespace = "comm"] #[cxx_name = "voidCallback"] fn void_callback(error: String, promise_id: u32); #[namespace = "comm"] #[cxx_name = "boolCallback"] fn bool_callback(error: String, promise_id: u32, ret: bool); } // AES cryptography #[namespace = "comm"] unsafe extern "C++" { include!("RustAESCrypto.h"); #[allow(unused)] #[cxx_name = "aesGenerateKey"] fn generate_key(buffer: &mut [u8]) -> Result<()>; /// The first two argument aren't mutated but creation of Java ByteBuffer /// requires the underlying bytes to be mutable. #[allow(unused)] #[cxx_name = "aesEncrypt"] fn encrypt( key: &mut [u8], plaintext: &mut [u8], sealed_data: &mut [u8], ) -> Result<()>; /// The first two argument aren't mutated but creation of Java ByteBuffer /// requires the underlying bytes to be mutable. #[allow(unused)] #[cxx_name = "aesDecrypt"] fn decrypt( key: &mut [u8], sealed_data: &mut [u8], plaintext: &mut [u8], ) -> Result<()>; } // Comm Services Auth Metadata Emission #[namespace = "comm"] unsafe extern "C++" { include!("RustCSAMetadataEmitter.h"); #[allow(unused)] #[cxx_name = "sendAuthMetadataToJS"] fn send_auth_metadata_to_js( access_token: String, user_id: String, ) -> Result<()>; } // Backup extern "Rust" { #[cxx_name = "startBackupHandler"] fn start_backup_handler() -> Result<()>; #[cxx_name = "stopBackupHandler"] fn stop_backup_handler() -> Result<()>; #[cxx_name = "triggerBackupFileUpload"] fn trigger_backup_file_upload(); #[cxx_name = "createBackup"] fn create_backup( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, siwe_backup_msg: String, promise_id: u32, ); #[cxx_name = "createUserKeysBackup"] fn create_user_keys_backup( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, siwe_backup_msg: String, promise_id: u32, ); #[cxx_name = "restoreBackup"] fn restore_backup( backup_secret: String, backup_id: String, max_version: String, promise_id: u32, ); #[cxx_name = "restoreBackupData"] fn restore_backup_data( backup_id: String, backup_data_key: String, backup_log_data_key: String, max_version: String, promise_id: u32, ); #[cxx_name = "retrieveBackupKeys"] fn retrieve_backup_keys( backup_secret: String, backup_id: String, promise_id: u32, ); #[cxx_name = "retrieveLatestBackupInfo"] fn retrieve_latest_backup_info(user_identifier: String, promise_id: u32); } // Secure store #[namespace = "comm"] unsafe extern "C++" { include!("RustSecureStore.h"); #[allow(unused)] #[cxx_name = "secureStoreSet"] fn secure_store_set(key: &str, value: String) -> Result<()>; #[cxx_name = "secureStoreGet"] fn secure_store_get(key: &str) -> Result; } // C++ Backup creation #[namespace = "comm"] unsafe extern "C++" { include!("RustBackupExecutor.h"); #[cxx_name = "getBackupDirectoryPath"] fn get_backup_directory_path() -> Result; #[cxx_name = "getBackupFilePath"] fn get_backup_file_path( backup_id: &str, is_attachments: bool, ) -> Result; #[cxx_name = "getBackupLogFilePath"] fn get_backup_log_file_path( backup_id: &str, log_id: &str, is_attachments: bool, ) -> Result; #[cxx_name = "getBackupUserKeysFilePath"] fn get_backup_user_keys_file_path(backup_id: &str) -> Result; #[cxx_name = "getSIWEBackupMessagePath"] fn get_siwe_backup_message_path(backup_id: &str) -> Result; #[cxx_name = "createMainCompaction"] fn create_main_compaction(backup_id: &str, future_id: usize); #[cxx_name = "restoreFromMainCompaction"] fn restore_from_main_compaction( main_compaction_path: &str, main_compaction_encryption_key: &str, max_version: &str, future_id: usize, ); #[cxx_name = "restoreFromBackupLog"] fn restore_from_backup_log(backup_log: Vec, future_id: usize); + + #[cxx_name = "setBackupID"] + fn set_backup_id(backup_id: &str, future_id: usize); } // Future handling from C++ extern "Rust" { #[cxx_name = "resolveUnitFuture"] fn resolve_unit_future(future_id: usize); #[cxx_name = "rejectFuture"] fn reject_future(future_id: usize, error: String); } } #[derive( Debug, derive_more::Display, derive_more::From, derive_more::Error, )] pub enum Error { #[display(fmt = "{}", "_0.message()")] TonicGRPC(Status), #[display(fmt = "{}", "_0")] SerdeJson(serde_json::Error), #[display(fmt = "Missing response data")] MissingResponseData, #[display(fmt = "{}", "_0")] GRPClient(grpc_clients::error::Error), } #[cfg(test)] #[allow(clippy::assertions_on_constants)] mod tests { use super::{BACKUP_SOCKET_ADDR, CODE_VERSION, IDENTITY_SOCKET_ADDR}; #[test] fn test_code_version_exists() { assert!(CODE_VERSION > 0); } #[test] fn test_identity_socket_addr_exists() { assert!(!IDENTITY_SOCKET_ADDR.is_empty()); assert!(!BACKUP_SOCKET_ADDR.is_empty()); } }