diff --git a/Cargo.lock b/Cargo.lock index 43a606a..6c62503 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -495,7 +495,7 @@ dependencies = [ [[package]] name = "signal-decrypt-backup-wasm" -version = "0.1.1" +version = "0.3.0" dependencies = [ "aes", "base64", diff --git a/Cargo.toml b/Cargo.toml index 1d3e272..5f8111a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "signal-decrypt-backup-wasm" -version = "0.1.1" +version = "0.3.0" edition = "2021" description = "Get the raw database from your Signal backup. Written for webassembly" repository = "https://git.duskflower.dev/duskflower/signal-decrypt-backup-wasm" diff --git a/build.sh b/build.sh index 8f41923..321e22c 100755 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -wasm-pack build --scope duskflower --release --target bundler +wasm-pack build --scope duskflower --release --weak-refs --target bundler jq '. |= . + {"publishConfig": {"registry": "https://git.duskflower.dev/api/packages/duskflower/npm/"}}' pkg/package.json > pkg/package.json.tmp && mv pkg/package.json.tmp pkg/package.json diff --git a/src/lib.rs b/src/lib.rs index 3b4614d..dbf8972 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,19 +39,6 @@ pub mod signal { type HmacSha256 = Hmac; -#[wasm_bindgen] -pub struct DecryptionResult { - database_statements: Vec, -} - -#[wasm_bindgen] -impl DecryptionResult { - #[wasm_bindgen(getter)] - pub fn database_statements(&self) -> Vec { - self.database_statements.clone() - } -} - // Add position field to ByteReader struct ByteReader { data: Vec, @@ -356,6 +343,8 @@ pub struct BackupDecryptor { header_data: Option, initialisation_vector: Option>, database_statements: Vec, + // how many statements were already passed back to JS for partial passing + returned_database_statements_length: usize, ciphertext_buf: Vec, plaintext_buf: Vec, total_file_size: usize, @@ -380,6 +369,7 @@ impl BackupDecryptor { header_data: None, initialisation_vector: None, database_statements: Vec::new(), + returned_database_statements_length: 0, ciphertext_buf: Vec::new(), plaintext_buf: Vec::new(), total_file_size: 0, @@ -513,8 +503,6 @@ impl BackupDecryptor { Err(e) => return Err(e), }; - // if we got to an attachment, but there we demand more data, it will be faulty, because we try to decrypt the frame although we would need - // to decrypt the attachment match decrypt_frame( &mut self.reader, hmac, @@ -524,6 +512,8 @@ impl BackupDecryptor { frame_length, ) { Ok(None) => { + // here we have to reset as well because the hmac depends on the length decryption which should therefore happen next time on the correct bits + self.reader.set_position(initial_reader_position); return Ok(true); } Ok(Some(backup_frame)) => { @@ -622,9 +612,17 @@ impl BackupDecryptor { } #[wasm_bindgen] - pub fn finish(self) -> Result { - Ok(DecryptionResult { - database_statements: self.database_statements, - }) + pub fn get_new_decrypted_statements(&mut self) -> Result, JsValue> { + let result = + Ok(self.database_statements[self.returned_database_statements_length..].to_vec()); + + self.returned_database_statements_length = self.database_statements.len(); + + result + } + + #[wasm_bindgen] + pub fn finish(self) -> Result, JsValue> { + Ok(self.database_statements.clone()) } } diff --git a/test/src/App.tsx b/test/src/App.tsx index 934806f..1f562ff 100644 --- a/test/src/App.tsx +++ b/test/src/App.tsx @@ -6,7 +6,8 @@ import { db } from "./db"; export async function decryptBackup( file: File, passphrase: string, - progressCallback: (progres: number) => void, + progressCallback: (progress: number) => void, + statementsCallback?: (statements: string[]) => void, ) { const fileSize = file.size; console.log("Starting decryption of file size:", fileSize); @@ -30,53 +31,30 @@ export async function decryptBackup( while (!done) { try { done = decryptor.process_chunk(passphrase); - } catch (e) { - console.error("Error processing chunk:", e); - throw e; + } catch (error) { + console.error("Error processing chunk:", error); + throw error; } } + statementsCallback?.(decryptor.get_new_decrypted_statements()); + offset += chunkSize; - // console.log(`Completed chunk, new offset: ${offset}`); - // if (performance.memory) { - // const memoryInfo = performance.memory; - // console.log(`Total JS Heap Size: ${memoryInfo.totalJSHeapSize} bytes`); - // console.log(`Used JS Heap Size: ${memoryInfo.usedJSHeapSize} bytes`); - // console.log(`JS Heap Size Limit: ${memoryInfo.jsHeapSizeLimit} bytes`); - // } else { - // console.log("Memory information is not available in this environment."); - // } } - // console.log("All chunks processed, finishing up"); const result = decryptor.finish(); - // decryptor.free(); - return result; - } catch (e) { - console.error("Decryption failed:", e); - throw e; + } catch (error) { + console.error("Decryption failed:", error); + throw error; } } async function decrypt(file: File, passphrase: string) { try { const result = await decryptBackup(file, passphrase, console.info); - - // console.log(result, result.database_bytes); - - // console.log("Database bytes length:", result.databaseBytes.length); - // console.log( - // "Database bytes as string (partly)", - // new TextDecoder().decode(result.database_bytes.slice(0, 1024 * 50)), - // ); - - // console.log(result.database_statements); - return result; - // console.log("Preferences:", result.preferences); - // console.log("Key values:", result.keyValues); } catch (error) { console.error("Decryption failed:", error); } @@ -86,14 +64,19 @@ const App: Component = () => { const [passphrase, setPassphrase] = createSignal(""); const [backupFile, setBackupFile] = createSignal(); + let executed = 0; + createEffect( on( backupFile, (currentBackupFile) => { if (currentBackupFile) { - decrypt(currentBackupFile, passphrase()).then((result) => { - if (result) { - for (const statement of result.database_statements) { + decryptBackup( + currentBackupFile, + passphrase(), + console.info, + (statements) => { + for (const statement of statements) { try { console.log("executing"); db.exec(statement); @@ -104,7 +87,11 @@ const App: Component = () => { } } - console.log("All statements executed"); + executed += statements.length; + }, + ).then((result) => { + if (result) { + console.log("All statements executed: ", result.length, executed); console.log( db.exec("SELECT * from message", { @@ -133,6 +120,7 @@ const App: Component = () => { { setBackupFile(event.currentTarget.files?.[0]); }}