diff --git a/native/native_rust_library/src/backup.rs b/native/native_rust_library/src/backup.rs index e8a1b1ccf..f4e3bb557 100644 --- a/native/native_rust_library/src/backup.rs +++ b/native/native_rust_library/src/backup.rs @@ -1,189 +1,245 @@ -use crate::argon2_tools::compute_backup_key; -use crate::argon2_tools::compute_backup_key_str; -use crate::constants::aes; -use crate::constants::secure_store; +use crate::argon2_tools::{compute_backup_key, compute_backup_key_str}; +use crate::constants::{aes, secure_store}; use crate::ffi::secure_store_get; use crate::handle_string_result_as_callback; use crate::BACKUP_SOCKET_ADDR; use crate::RUNTIME; -use backup_client::BackupDescriptor; -use backup_client::LatestBackupIDResponse; -use backup_client::RequestedData; -use backup_client::{BackupClient, BackupData, UserIdentity}; +use backup_client::{ + BackupClient, BackupData, BackupDescriptor, DownloadLogsRequest, + LatestBackupIDResponse, LogWSResponse, RequestedData, SinkExt, StreamExt, + UploadLogRequest, UserIdentity, +}; use serde::{Deserialize, Serialize}; use serde_json::json; use std::error::Error; + pub mod ffi { use crate::handle_void_result_as_callback; use super::*; pub fn create_backup_sync( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, user_data: String, promise_id: u32, ) { RUNTIME.spawn(async move { let result = create_backup( backup_id, backup_secret, pickle_key, pickled_account, user_data, ) .await; handle_void_result_as_callback(result, promise_id); }); } pub fn restore_backup_sync(backup_secret: String, promise_id: u32) { RUNTIME.spawn(async move { let result = restore_backup(backup_secret).await; handle_string_result_as_callback(result, promise_id); }); } } pub async fn create_backup( backup_id: String, backup_secret: String, pickle_key: String, pickled_account: String, user_data: String, ) -> Result<(), Box> { let mut backup_key = compute_backup_key(backup_secret.as_bytes(), backup_id.as_bytes())?; let mut user_data = user_data.into_bytes(); let mut backup_data_key = [0; aes::KEY_SIZE]; crate::ffi::generate_key(&mut backup_data_key)?; let encrypted_user_data = encrypt(&mut backup_data_key, &mut user_data)?; let user_keys = UserKeys { backup_data_key, pickle_key, pickled_account, }; let encrypted_user_keys = user_keys.encrypt(&mut backup_key)?; let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?; let user_identity = get_user_identity_from_secure_store()?; let backup_data = BackupData { - backup_id, + backup_id: backup_id.clone(), user_data: encrypted_user_data, user_keys: encrypted_user_keys, attachments: Vec::new(), }; backup_client .upload_backup(&user_identity, backup_data) .await?; + let (tx, rx) = backup_client + .upload_logs(&user_identity, &backup_id) + .await + .unwrap(); + + tokio::pin!(tx); + tokio::pin!(rx); + + let log_data = UploadLogRequest { + log_id: 1, + content: (1..100).collect(), + attachments: None, + }; + tx.send(log_data.clone()).await?; + match rx.next().await { + Some(Ok(1)) => (), + response => { + return Err(Box::new(InvalidWSLogResponse(format!("{response:?}")))) + } + }; + Ok(()) } pub async fn restore_backup( backup_secret: String, ) -> Result> { let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?; let user_identity = get_user_identity_from_secure_store()?; let latest_backup_descriptor = BackupDescriptor::Latest { username: user_identity.user_id.clone(), }; let backup_id_response = backup_client .download_backup_data(&latest_backup_descriptor, RequestedData::BackupID) .await?; let LatestBackupIDResponse { backup_id } = serde_json::from_slice(&backup_id_response)?; let mut backup_key = compute_backup_key_str(&backup_secret, &backup_id)?; let mut encrypted_user_keys = backup_client .download_backup_data(&latest_backup_descriptor, RequestedData::UserKeys) .await?; let mut user_keys = UserKeys::from_encrypted(&mut encrypted_user_keys, &mut backup_key)?; let backup_data_descriptor = BackupDescriptor::BackupID { - backup_id, - user_identity, + backup_id: backup_id.clone(), + user_identity: user_identity.clone(), }; let mut encrypted_user_data = backup_client .download_backup_data(&backup_data_descriptor, RequestedData::UserData) .await?; let user_data = decrypt(&mut user_keys.backup_data_key, &mut encrypted_user_data)?; let user_data: serde_json::Value = serde_json::from_slice(&user_data)?; + let (tx, rx) = backup_client + .download_logs(&user_identity, &backup_id) + .await + .unwrap(); + + tokio::pin!(tx); + tokio::pin!(rx); + + tx.send(DownloadLogsRequest { from_id: None }) + .await + .unwrap(); + + match rx.next().await { + Some(Ok(LogWSResponse::LogDownload { + log_id: 1, + content, + attachments: None, + })) + if content == (1..100).collect::>() => {} + response => { + return Err(Box::new(InvalidWSLogResponse(format!("{response:?}")))) + } + }; + + match rx.next().await { + Some(Ok(LogWSResponse::LogDownloadFinished { last_log_id: None })) => {} + response => { + return Err(Box::new(InvalidWSLogResponse(format!("{response:?}")))) + } + }; + Ok( json!({ "userData": user_data, "pickleKey": user_keys.pickle_key, "pickledAccount": user_keys.pickled_account, }) .to_string(), ) } 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)?, }) } #[derive(Debug, Serialize, Deserialize)] struct UserKeys { backup_data_key: [u8; 32], 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) } + +#[derive(Debug, derive_more::Display)] +struct InvalidWSLogResponse(String); +impl Error for InvalidWSLogResponse {}