diff --git a/rust/src/commands/export.rs b/rust/src/commands/export.rs index b82d12fb80..4874e882da 100644 --- a/rust/src/commands/export.rs +++ b/rust/src/commands/export.rs @@ -1,7 +1,7 @@ use crate::Result; use crate::api::client::ApiClient; use crate::api::methods::ApiMethods; -use crate::crypto::{decrypt_stream, init as crypto_init, secret_box_open}; +use crate::crypto::{decrypt_file_data, decrypt_stream, init as crypto_init, secret_box_open}; use crate::models::{account::Account, metadata::FileMetadata}; use crate::storage::Storage; use base64::Engine; @@ -217,7 +217,8 @@ async fn export_account(storage: &Storage, account: &Account) -> Result<()> { }; // Decrypt the file data using streaming XChaCha20-Poly1305 - let decrypted = match decrypt_stream(&encrypted_data, &file_nonce, &file_key) { + // Use chunked decryption for large files + let decrypted = match decrypt_file_data(&encrypted_data, &file_nonce, &file_key) { Ok(data) => data, Err(e) => { log::error!("Failed to decrypt file {}: {}", file.id, e); diff --git a/rust/src/crypto/stream.rs b/rust/src/crypto/stream.rs index 8a972f474a..b5f9686328 100644 --- a/rust/src/crypto/stream.rs +++ b/rust/src/crypto/stream.rs @@ -104,9 +104,30 @@ pub fn decrypt_stream(ciphertext: &[u8], header: &[u8], key: &[u8]) -> Result Result> { - // For large files, the Go implementation uses streaming with chunks - // For now, we'll decrypt the whole file at once which works for most files - decrypt_stream(encrypted_data, header, key) + // Buffer size matching Go implementation: 4MB + overhead + const CHUNK_SIZE: usize = + 4 * 1024 * 1024 + sodium::crypto_secretstream_xchacha20poly1305_ABYTES as usize; + + let mut decryptor = StreamDecryptor::new(key, header)?; + let mut result = Vec::with_capacity(encrypted_data.len()); + + let mut offset = 0; + while offset < encrypted_data.len() { + let chunk_end = std::cmp::min(offset + CHUNK_SIZE, encrypted_data.len()); + let chunk = &encrypted_data[offset..chunk_end]; + + let (plaintext, tag) = decryptor.pull(chunk)?; + result.extend_from_slice(&plaintext); + + offset = chunk_end; + + // Check if this was the final chunk + if tag == TAG_FINAL { + break; + } + } + + Ok(result) }